Commit be1380d7 be1380d7aa53c031a237b5e993187733eab3fa40 by Sergey Poznyakoff

Improve the algorithm for URL look up in wickets.

The new algorithm always returns the best match, i.e. the
one with the smallest number of wildcard comparisons.

* include/mailutils/auth.h (mu_wicket_stream_match_url)
(mu_wicket_file_match_url): New protos.
Remove unnecessary 'extern' qualifiers.
* include/mailutils/url.h (mu_url_is_ticket): Remove.
(mu_url_matches_ticket): New proto.
* libmailutils/base/url.c (mu_url_parse): Fix name corruption during
password obfuscation.
(defined): Remove static function.
(mu_url_is_ticket): Remove function.
(mu_url_matches_ticket): New function.
* libmailutils/base/wicket.c (get_ticket_url): Remove.
(mu_wicket_stream_match_url)
(mu_wicket_file_match_url): New functions.
(file_ticket_get_cred): Use mu_wicket_file_match_url.

* libmailutils/tests/wicket.c: New file.
* libmailutils/tests/Wicketfile: New file.
* libmailutils/tests/wicket.at: New file.
* libmailutils/tests/Makefile.am (noinst_PROGRAMS): Add wicket.
(EXTRA_DIST): Add Wicketfile
(TESTSUITE_AT): Add wicket.at
* libmailutils/tests/testsuite.at: Include wicket.at
* libmailutils/tests/.gitignore: Update.
1 parent d992b1b4
......@@ -25,56 +25,64 @@
extern "C" {
#endif
extern int mu_ticket_create (mu_ticket_t *, void *);
extern int mu_ticket_ref (mu_ticket_t);
extern int mu_ticket_unref (mu_ticket_t);
extern void mu_ticket_destroy (mu_ticket_t *);
extern int mu_ticket_set_destroy (mu_ticket_t,
void (*) (mu_ticket_t), void *);
extern void *mu_ticket_get_owner (mu_ticket_t);
extern int mu_ticket_get_cred (mu_ticket_t ticket,
mu_url_t url, const char *challenge,
char **pplain, mu_secret_t *psec);
extern int mu_ticket_set_get_cred (mu_ticket_t,
int (*) (mu_ticket_t, mu_url_t,
const char *,
char **, mu_secret_t *),
void *);
extern int mu_ticket_set_data (mu_ticket_t, void *, void *owner);
extern void *mu_ticket_get_data (mu_ticket_t);
extern int mu_ticket_set_secret (mu_ticket_t ticket, mu_secret_t secret);
int mu_ticket_create (mu_ticket_t *, void *);
int mu_ticket_ref (mu_ticket_t);
int mu_ticket_unref (mu_ticket_t);
void mu_ticket_destroy (mu_ticket_t *);
int mu_ticket_set_destroy (mu_ticket_t,
void (*) (mu_ticket_t), void *);
void *mu_ticket_get_owner (mu_ticket_t);
int mu_ticket_get_cred (mu_ticket_t ticket,
mu_url_t url, const char *challenge,
char **pplain, mu_secret_t *psec);
int mu_ticket_set_get_cred (mu_ticket_t,
int (*) (mu_ticket_t, mu_url_t,
const char *,
char **, mu_secret_t *),
void *);
int mu_ticket_set_data (mu_ticket_t, void *, void *owner);
void *mu_ticket_get_data (mu_ticket_t);
int mu_ticket_set_secret (mu_ticket_t ticket, mu_secret_t secret);
int mu_ticket_set_plain (mu_ticket_t ticket, const char *text);
extern int mu_authority_create (mu_authority_t *, mu_ticket_t, void *);
extern void mu_authority_destroy (mu_authority_t *, void *);
extern void *mu_authority_get_owner (mu_authority_t);
extern int mu_authority_set_ticket (mu_authority_t, mu_ticket_t);
extern int mu_authority_get_ticket (mu_authority_t, mu_ticket_t *);
extern int mu_authority_authenticate (mu_authority_t);
extern int mu_authority_set_authenticate (mu_authority_t,
int (*_authenticate) (mu_authority_t), void *);
int mu_authority_create (mu_authority_t *, mu_ticket_t, void *);
void mu_authority_destroy (mu_authority_t *, void *);
void *mu_authority_get_owner (mu_authority_t);
int mu_authority_set_ticket (mu_authority_t, mu_ticket_t);
int mu_authority_get_ticket (mu_authority_t, mu_ticket_t *);
int mu_authority_authenticate (mu_authority_t);
int mu_authority_set_authenticate (mu_authority_t,
int (*_authenticate) (mu_authority_t),
void *);
extern int mu_authority_create_null (mu_authority_t *pauthority, void *owner);
int mu_authority_create_null (mu_authority_t *pauthority, void *owner);
extern int mu_wicket_create (mu_wicket_t *);
extern int mu_wicket_get_ticket (mu_wicket_t wicket, const char *user,
int mu_wicket_create (mu_wicket_t *);
int mu_wicket_get_ticket (mu_wicket_t wicket, const char *user,
mu_ticket_t *pticket);
extern int mu_wicket_ref (mu_wicket_t wicket);
extern int mu_wicket_unref (mu_wicket_t wicket);
extern void mu_wicket_destroy (mu_wicket_t *pwicket);
extern int mu_wicket_set_destroy (mu_wicket_t wicket,
int mu_wicket_ref (mu_wicket_t wicket);
int mu_wicket_unref (mu_wicket_t wicket);
void mu_wicket_destroy (mu_wicket_t *pwicket);
int mu_wicket_set_destroy (mu_wicket_t wicket,
void (*_destroy) (mu_wicket_t));
extern int mu_wicket_set_data (mu_wicket_t wicket, void *data);
extern void *mu_wicket_get_data (mu_wicket_t wicket);
extern int mu_wicket_set_get_ticket (mu_wicket_t wicket,
int mu_wicket_set_data (mu_wicket_t wicket, void *data);
void *mu_wicket_get_data (mu_wicket_t wicket);
int mu_wicket_set_get_ticket (mu_wicket_t wicket,
int (*_get_ticket) (mu_wicket_t, void *,
const char *, mu_ticket_t *));
extern int mu_file_wicket_create (mu_wicket_t *pwicket, const char *filename);
const char *, mu_ticket_t *));
int mu_file_wicket_create (mu_wicket_t *pwicket, const char *filename);
struct mu_debug_locus;
int mu_wicket_stream_match_url (mu_stream_t stream, struct mu_debug_locus *loc,
mu_url_t url, mu_url_t *pticket_url);
int mu_wicket_file_match_url (const char *name, mu_url_t url,
mu_url_t *pticket_url);
#ifdef __cplusplus
}
#endif
......
......@@ -104,7 +104,7 @@ int mu_url_is_same_port (mu_url_t, mu_url_t);
char *mu_url_decode_len (const char *s, size_t len);
char *mu_url_decode (const char *s);
int mu_url_is_ticket (mu_url_t ticket, mu_url_t url);
int mu_url_matches_ticket (mu_url_t ticket, mu_url_t url, int *wcn);
int mu_url_init (mu_url_t url, int port, const char *scheme);
#ifdef __cplusplus
......
......@@ -316,13 +316,18 @@ mu_url_parse (mu_url_t url)
char *newname;
memset (url->name + pstart, 0, plen);
newname = realloc (url->name, len);
if (!newname)
goto CLEANUP;
if (len > nlen + 1)
{
newname = realloc (url->name, len);
if (!newname)
goto CLEANUP;
url->name = newname;
}
else
newname = url->name;
memmove (newname + pstart + PASS_REPL_LEN, newname + pstart + plen,
nlen - (pstart + plen) + 1);
memcpy (newname + pstart, PASS_REPL, PASS_REPL_LEN);
url->name = newname;
}
/* Dup the strings we found. We wouldn't have to do this
......@@ -959,44 +964,68 @@ mu_url_decode (const char *s)
return mu_url_decode_len (s, strlen (s));
}
static int
defined (const char *s)
{
if (s && strcmp ("*", s) != 0)
return 1;
return 0;
}
#define is_wildcard(s) ((s)[0] == '*' && s[1] == 0)
int
mu_url_is_ticket (mu_url_t ticket, mu_url_t url)
mu_url_matches_ticket (mu_url_t ticket, mu_url_t url, int *pwc)
{
if (!ticket || !url)
int wcnt = 0;
if (is_wildcard (ticket->scheme))
wcnt++;
else if (mu_c_strcasecmp (ticket->scheme, url->scheme))
return 0;
/* If ticket has a scheme, host, port, or path, then the queries
equivalent must be defined and match. */
if (defined (ticket->scheme))
if (ticket->flags & MU_URL_HOST)
{
if (!url->scheme || mu_c_strcasecmp (ticket->scheme, url->scheme) != 0)
if (is_wildcard (ticket->host))
wcnt++;
else if (url->flags & MU_URL_HOST)
{
if (mu_c_strcasecmp (ticket->host, url->host))
/* FIXME: Compare IP addresses */
return 0;
}
else
return 0;
}
if (defined (ticket->host))
else
wcnt++;
if (ticket->flags & MU_URL_PORT)
{
if (!url->host || mu_c_strcasecmp (ticket->host, url->host) != 0)
return 0;
/* FIXME: No way to put a wildcard in the ticket file */
if (url->port & MU_URL_PORT)
{
if (ticket->port != url->port)
return 0;
else
wcnt++;
}
}
if (ticket->port && ticket->port != url->port)
return 0;
/* If ticket has a user or pass, but url doesn't, that's OK, we were
urling for this info. But if url does have a user/pass, it
must match the ticket. */
if (url->user)
else
wcnt++;
if (ticket->flags & MU_URL_USER)
{
if (defined (ticket->user) && strcmp (ticket->user, url->user) != 0)
return 0;
if (is_wildcard (ticket->user))
wcnt += 2;
/* If ticket has a user or pass, but url doesn't, that's OK, we were
looking for this info. But if url does have a user/pass, it
must match the ticket. */
else if (url->flags & MU_URL_USER)
{
if (strcmp (ticket->user, url->user))
return 0;
}
}
else
wcnt++;
/* Guess it matches. */
if (pwc)
*pwc = wcnt;
return 1;
}
......
......@@ -34,6 +34,9 @@
#include <mailutils/util.h>
#include <mailutils/mu_auth.h>
#include <mailutils/stream.h>
#include <mailutils/cstr.h>
#include <mailutils/nls.h>
#include <mailutils/errno.h>
#include <mailutils/sys/auth.h>
#include <mailutils/sys/url.h>
......@@ -50,7 +53,8 @@ mu_wicket_create (mu_wicket_t *pwicket)
}
int
mu_wicket_get_ticket (mu_wicket_t wicket, const char *user, mu_ticket_t *pticket)
mu_wicket_get_ticket (mu_wicket_t wicket, const char *user,
mu_ticket_t *pticket)
{
if (!wicket)
return EINVAL;
......@@ -168,8 +172,6 @@ file_ticket_destroy (mu_ticket_t ticket)
}
}
static int get_ticket_url (mu_ticket_t ticket, mu_url_t url, mu_url_t *pticket_url);
int
file_ticket_get_cred (mu_ticket_t ticket, mu_url_t url, const char *challenge,
char **pplain, mu_secret_t *psec)
......@@ -178,7 +180,7 @@ file_ticket_get_cred (mu_ticket_t ticket, mu_url_t url, const char *challenge,
if (!ft->tickurl)
{
int rc = get_ticket_url (ticket, url, &ft->tickurl);
int rc = mu_wicket_file_match_url (ft->filename, url, &ft->tickurl);
if (rc)
return rc;
}
......@@ -241,37 +243,28 @@ _file_wicket_get_ticket (mu_wicket_t wicket, void *data,
return 0;
}
static int
get_ticket_url (mu_ticket_t ticket, mu_url_t url, mu_url_t *pticket_url)
int
mu_wicket_stream_match_url (mu_stream_t stream, struct mu_debug_locus *loc,
mu_url_t url, mu_url_t *pticket_url)
{
mu_stream_t stream;
struct file_ticket *ft = mu_ticket_get_data (ticket);
int rc;
mu_url_t u = NULL;
char *buf = NULL;
size_t bufsize = 0;
size_t len;
rc = mu_file_stream_create (&stream, ft->filename, MU_STREAM_READ);
if (rc)
return rc;
mu_url_t pret = NULL;
int weight = 0;
int line = loc->line;
while ((rc = mu_stream_getline (stream, &buf, &bufsize, &len)) == 0
&& len > 0)
{
char *p;
int err;
int n;
/* Truncate a trailing newline. */
if (len && buf[len - 1] == '\n')
buf[--len] = 0;
/* Skip leading spaces */
for (p = buf; *p == ' ' || *p == '\t'; p++)
;
/* Skip trailing spaces */
for (; len > 0 && (p[len-1] == ' ' || p[len-1] == '\t'); )
p[--len] = 0;
loc->line++;
p = mu_str_stripws (buf);
/* Skip empty lines and comments. */
if (*p == 0 || *p == '#')
......@@ -280,43 +273,42 @@ get_ticket_url (mu_ticket_t ticket, mu_url_t url, mu_url_t *pticket_url)
if ((err = mu_url_create (&u, p)) != 0)
{
/* Skip erroneous entry */
/* FIXME: Error message */
mu_error (_("%s:%u: cannot create URL: %s"),
loc->file, loc->line, mu_strerror (err));
continue;
}
if ((err = mu_url_parse (u)) != 0)
{
/* FIXME: See above */
mu_error (_("%s:%u: cannot parse URL: %s"),
loc->file, loc->line, mu_strerror (err));
mu_url_destroy (&u);
continue;
}
if (!mu_url_is_ticket (u, url))
if (!mu_url_matches_ticket (u, url, &n))
{
mu_url_destroy (&u);
continue;
}
if (ft->user)
if (!pret || n < weight)
{
if (u->name && strcmp (u->name, "*") != 0
&& strcmp (ft->user, u->name) != 0)
{
mu_url_destroy (&u);
continue;
}
pret = u;
weight = n;
line = loc->line;
if (weight == 0)
break;
}
break;
}
mu_stream_close (stream);
free (buf);
mu_stream_destroy (&stream);
if (rc == 0)
{
if (u)
*pticket_url = u;
if (pret)
{
*pticket_url = pret;
loc->line = line;
}
else
rc = MU_ERR_NOENT;
}
......@@ -325,6 +317,25 @@ get_ticket_url (mu_ticket_t ticket, mu_url_t url, mu_url_t *pticket_url)
}
int
mu_wicket_file_match_url (const char *name, mu_url_t url,
mu_url_t *pticket_url)
{
mu_stream_t stream;
int rc;
struct mu_debug_locus loc;
rc = mu_file_stream_create (&stream, name, MU_STREAM_READ);
if (rc)
return rc;
loc.file = name;
loc.line = 0;
rc = mu_wicket_stream_match_url (stream, &loc, url, pticket_url);
mu_stream_close (stream);
mu_stream_destroy (&stream);
return rc;
}
int
mu_file_wicket_create (mu_wicket_t *pwicket, const char *filename)
{
mu_wicket_t wicket;
......
......@@ -12,3 +12,4 @@ fltst
listop
mailcap
url-parse
wicket
......
......@@ -47,11 +47,12 @@ noinst_PROGRAMS = \
fltst\
listop\
mailcap\
url-parse
url-parse\
wicket
LDADD = ${MU_LIB_MAILUTILS}
EXTRA_DIST += Encode Decode
EXTRA_DIST += Encode Decode Wicketfile
## ------------ ##
## Test suite. ##
......@@ -68,7 +69,8 @@ TESTSUITE_AT = \
list.at\
mailcap.at\
testsuite.at\
url.at
url.at\
wicket.at
TESTSUITE = $(srcdir)/testsuite
M4=m4
......
# A test wicket file.
*://foo:bar@localhost
pop://*:foo;AUTH=+APOP@localhost
pop://gray:test@localhost
smtp://gray:guessme@localhost
smtp://gray:adiviname@gnu.org
pop://gray:secret@gnu.org
......@@ -63,4 +63,5 @@ m4_include([base64d.at])
m4_include([decode2047.at])
m4_include([encode2047.at])
m4_include([fromflt.at])
m4_include([wicket.at])
......
# This file is part of GNU Mailutils. -*- Autotest -*-
# Copyright (C) 2010 Free Software Foundation, Inc.
#
# GNU Mailutils is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3, or (at
# your option) any later version.
#
# GNU Mailutils is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
dnl ------------------------------------------------------------
dnl TESTWICKET(URL, [KW = `'], [STDOUT = `'],
dnl [STDERR = `'], [RUN-IF-FAIL], [RUN-IF-PASS])
dnl
m4_pushdef([TESTWICKET],[
AT_SETUP([Wicket: [$1]])
AT_KEYWORDS([wicket wicket-$2])
AT_CHECK([wicket $abs_top_srcdir/libmailutils/tests/Wicketfile "$1" | \
sed "s|$abs_top_srcdir/libmailutils/tests/Wicketfile|Wicketfile|"],
[0],
[$3],[$4],[$5],[$6])
AT_CLEANUP])
TESTWICKET([pop://localhost],[00],
[pop://localhost matches pop://gray:***@localhost at Wicketfile:4
])
TESTWICKET([pop://smith@localhost],[01],
[pop://smith@localhost matches pop://*:***;AUTH=+APOP@localhost at Wicketfile:3
])
TESTWICKET([pop://foo@localhost],[02],
[pop://foo@localhost matches *://foo:***@localhost at Wicketfile:2
])
TESTWICKET([pop://gnu.org],[03],
[pop://gnu.org matches pop://gray:***@gnu.org at Wicketfile:7
])
TESTWICKET([smtp://gnu.org],[04],
[smtp://gnu.org matches smtp://gray:***@gnu.org at Wicketfile:6
])
TESTWICKET([smtp://gray@gnu.org],[05],
[smtp://gray@gnu.org matches smtp://gray:***@gnu.org at Wicketfile:6
])
TESTWICKET([smtp://localhost],[06],
[smtp://localhost matches smtp://gray:***@localhost at Wicketfile:5
])
\ No newline at end of file
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2000, 2001, 2002, 2005, 2007, 2010 Free Software
Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GNU Mailutils is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <unistd.h>
#include <stdio.h>
#include <mailutils/mailutils.h>
const char *name;
mu_stream_t stream;
void
match_string (const char *str)
{
int rc;
mu_url_t u, url;
struct mu_debug_locus loc;
if ((rc = mu_url_create (&u, str)) != 0)
{
fprintf (stderr, "mu_url_create %s ERROR: [%d] %s",
str, rc, mu_strerror (rc));
return;
}
if ((rc = mu_url_parse (u)) != 0)
{
fprintf (stderr, "%s\n", mu_errname (rc));
return;
}
MU_ASSERT (mu_stream_seek (stream, 0, MU_SEEK_SET, NULL));
loc.file = name;
loc.line = 0;
rc = mu_wicket_stream_match_url (stream, &loc, u, &url);
switch (rc)
{
case 0:
printf ("%s matches %s at %s:%d\n",
mu_url_to_string (u), mu_url_to_string (url),
loc.file, loc.line);
mu_url_destroy (&url);
break;
case MU_ERR_NOENT:
printf ("no matches for %s\n", mu_url_to_string (u));
break;
default:
mu_error ("mu_wicket_stream_match_url: %s", mu_strerror (rc));
exit (1);
}
mu_url_destroy (&u);
}
int
main (int argc, char **argv)
{
if (argc < 2)
{
fprintf (stderr, "usage: %s filename [url [url...]]\n", argv[0]);
return 1;
}
name = argv[1];
MU_ASSERT (mu_file_stream_create (&stream, name, MU_STREAM_READ));
if (argc > 2)
{
int i;
for (i = 2; i < argc; i++)
match_string (argv[i]);
}
else
{
mu_stream_t in;
char *buf = NULL;
size_t size = 0, n;
int rc;
MU_ASSERT (mu_stdio_stream_create (&in, MU_STDIN_FD, 0));
while ((rc = mu_stream_getline (in, &buf, &size, &n)) == 0
&& n > 0)
match_string (mu_str_stripws (buf));
free (buf);
mu_stream_destroy (&in);
}
mu_stream_destroy (&stream);
return 0;
}