/* $Id: omacutil.c,v 1.2 2006/03/23 07:14:49 dm Exp $ */
/*
*
* Copyright (C) 2004 David Mazieres (dm@uun.org)
*
* This program 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 2, or (at
* your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
*/
#include "avutil.h"
#include "getopt_long.h"
char *progname;
long opt_date;
char *opt_pwfile;
char *opt_expire;
char *opt_aux;
char *opt_sender;
char *opt_from;
char *opt_fromexp;
int opt_verbose = 0;
struct addrinfo {
u_int32_t time; /* Expiration time */
unsigned nonce; /* 24-bit nonce */
u_char reserved : 1;
u_char hasaux : 1; /* last 8 bytes hash of aux string */
u_char duration : 6; /* ceil (log_2 (seconds valid)) */
char aux[8]; /* must be zero or hash of aux string */
};
struct binaddr {
char time[4]; /* message timestamp */
char nonce[3]; /* nonce */
char rhd; /* reserved/hasaux/duration*/
char aux[8]; /* zero or hash of aux string */
};
static inline void
put3b (void *_dp, u_int32_t val)
{
unsigned char *dp = (unsigned char *) (_dp);
dp[0] = val >> 16;
dp[1] = val >> 8;
dp[2] = val;
}
static inline u_int32_t
get3b (const void *_dp)
{
const unsigned char *dp = (const unsigned char *) (_dp);
return dp[0] << 16 | dp[1] << 8 | dp[2];
}
static void
h2nai (struct binaddr *out, const struct addrinfo *in)
{
putint (out->time, in->time);
put3b (out->nonce, in->nonce);
out->rhd = in->duration;
if (in->hasaux) {
out->rhd |= 0x40;
memcpy (out->aux, in->aux, sizeof (out->aux));
}
else
bzero (out->aux, sizeof (out->aux));
}
static void
n2hai (struct addrinfo *out, const struct binaddr *in)
{
out->time = getint (in->time);
out->nonce = get3b (in->nonce);
out->duration = in->rhd & 0x3f;
out->hasaux = !!(in->rhd & 0x40);
out->reserved = !!(in->rhd & 0x80);
memcpy (out->aux, in->aux, sizeof (out->aux));
}
char *
getmacpw (FILE *pwfile)
{
static char hashbuf[20];
char pwbuf[300];
char *p;
sha1_ctx sha;
if (!fgets (pwbuf, sizeof (pwbuf), pwfile))
return NULL;
p = strchr (pwbuf, '\n');
if (!p) {
fprintf (stderr, "password file line too long\n");
exit (1);
}
*p = '\0';
sha1_init (&sha);
sha1_update (&sha, pwbuf, p - pwbuf);
sha1_final (&sha, (void *) hashbuf);
bzero (pwbuf, sizeof (pwbuf));
return hashbuf;
}
void
sha1_str (char *out, size_t outlen, const char *in)
{
sha1_ctx sha;
char hashbuf[sha1_hashsize];
sha1_init (&sha);
sha1_update (&sha, in, strlen (in));
sha1_final (&sha, (void *) hashbuf);
memcpy (out, hashbuf, outlen < sizeof (hashbuf) ? outlen : sizeof (hashbuf));
}
char *
mkaddr (const char *pw, time_t exp, const char *aux)
{
struct addrinfo ai;
struct binaddr bi;
struct timespec ts;
aes_ctx aes;
char *asc;
ai.time = exp;
clock_gettime (CLOCK_REALTIME, &ts);
ai.nonce = ts.tv_nsec >> 6;
ai.reserved = 0;
ai.hasaux = !!aux;
if (exp > opt_date) {
u_long dur = exp - opt_date;
int logdur = 0;
while (dur > 0) {
logdur++;
dur = dur >> 1;
}
ai.duration = logdur;
}
else
ai.duration = 0;
if (aux)
sha1_str (ai.aux, sizeof (ai.aux), aux);
h2nai (&bi, &ai);
aes_setkey (&aes, pw, 16);
aes_encrypt (&aes, &bi, &bi);
asc = armor32 (&bi, 16);
return asc;
}
void
do_gen (const char *pw, time_t exp, const char *aux)
{
char *asc = mkaddr (pw, exp, aux);
printf ("%s\n", asc);
free (asc);
}
void
do_check (FILE *pwfile, char *asc, const char *auxsrc)
{
char dbuf[16];
char *pw;
aes_ctx aes;
unsigned long now = opt_date;
int bad;
struct binaddr bi;
struct addrinfo ai;
char aux[sizeof (ai.aux)];
if (auxsrc)
sha1_str (aux, sizeof (aux), auxsrc);
else
bzero (aux, sizeof (aux));
if (dearmor32len (asc) != sizeof (dbuf)
|| (size_t) dearmor32 (dbuf, asc) != strlen (asc)) {
fprintf (stderr, "bad input format\n");
exit (1);
}
/* dearmor32 might ignore trailing bits, so double check we would
* have produce this output. (This only matters for security if you
* are imposing some sort of quote per token, in which case users
* could generate more valid looking tokens by manipulating the last
* character of the token.) */
pw = armor32 (dbuf, 16);
bad = strcmp (pw, asc);
free (pw);
if (bad) {
fprintf (stderr, "bad input format\n");
exit (1);
}
do {
bad = 0;
pw = getmacpw (pwfile);
if (!pw) {
fprintf (stderr, "bad mac\n");
exit (1);
}
aes_setkey (&aes, pw, 16);
bzero (pw, 20);
aes_decrypt (&aes, &bi, dbuf);
n2hai (&ai, &bi);
if (!auxsrc != !ai.hasaux)
bad++;
if (memcmp (aux, ai.aux, sizeof (aux)))
bad++;
if (opt_verbose) {
printf ("Decrypted to:\n");
printf (" Time: %lu\n", (unsigned long) ai.time);
printf (" Nonce: %u\n", ai.nonce);
printf ("reserved: %d\n", (int) ai.reserved);
printf (" hasaux: %d\n", (int) ai.hasaux);
printf ("duration: %d\n", (int) ai.duration);
printf (" aux: %s\n", bad ? "bad" : "ok");
}
if (now > ai.time || (ai.time - now) >> ai.duration)
bad++;
} while (bad);
bzero (&aes, sizeof (aes));
}
char *
firstpw (void)
{
char *ret;
FILE *pwfile = fopen (opt_pwfile, "r");
if (!pwfile) {
perror (opt_pwfile);
return NULL;
}
ret = getmacpw (pwfile);
fclose (pwfile);
return ret;
}
char *
mksender (long exp, const char *aux)
{
char *p, *q, *s, *sender;
s = opt_sender;
if (!s)
s = getenv ("MACUTIL_SENDER");
if (!s) {
fprintf (stderr, "%s: no MACUTIL_SENDER environment variable\n", progname);
return NULL;
}
p = strchr (s, '@');
if (!p)
p = s + strlen (s);
for (q = p; *q != '*'; q--)
if (q == s) {
fprintf (stderr, "%s: MACUTIL_SENDER must contain '*'\n", progname);
return NULL;
}
p = firstpw ();
if (!p)
return NULL;
sender = mkaddr (p, exp, aux);
bzero (p, 20);
p = sender;
sender = xmalloc ((q - s) + strlen (q) + strlen (p));
sprintf (sender, "%.*s%s%s", (int) (q - s), s, p, *q ? q + 1 : "");
free (p);
return sender;
}
int
do_sendmail (int argc, char **argv)
{
char *sendmail = getenv ("MACUTIL_SENDMAIL");
char *sender = NULL;
char **av;
long exp;
exp = parse_expire (opt_expire, opt_date);
if (exp != -1)
sender = mksender (exp, opt_aux);
av = xmalloc ((argc + 3) * sizeof (av[0]));
av[0] = sendmail ? sendmail : "sendmail";
av[1] = "-f";
av[2] = sender;
memcpy (av + 3, argv + 1, (argc - 1) * sizeof (av[0]));
av[argc+2] = NULL;
if (sender)
execvp (av[0], av);
else {
av[2] = av[0];
execvp (av[0], av + 2);
}
perror (av[0]);
exit (1);
}
static char *
default_macpass (void)
{
char defsuf[] = "/.avenger/.macpass";
char *home = getenv ("HOME");
char *ret;
if (!home) {
fprintf (stderr, "no HOME environment variable set\n");
exit (1);
}
ret = xmalloc (strlen (home) + sizeof (defsuf));
sprintf (ret, "%s%s", home, defsuf);
return ret;
}
static void usage (void) __attribute__ ((noreturn));
static void
usage (void)
{
fprintf (stderr, "usage: %s --gen [options]\n", progname);
fprintf (stderr, " %s --sender [options] address-template\n",
progname);
fprintf (stderr, " %s --check [options] nonce\n", progname);
fprintf (stderr, " %s --sendmail [sendmail options]\n", progname);
fprintf (stderr, "options are:\n"
" --passfile=file File containing password(s)\n"
" --expire=date Set expiration date with --gen\n"
" --date=date Act as if current time were date\n");
exit (1);
}
int
main (int argc, char **argv)
{
int mode = 0;
int c;
struct option o[] = {
{ "version", no_argument, NULL, 'v' },
{ "check", no_argument, &mode, 'c' },
{ "gen", no_argument, &mode, 'g' },
{ "passfile", required_argument, NULL, 'p' },
{ "expire", required_argument, NULL, 'e' },
{ "date", required_argument, NULL, 'd' },
{ "aux", required_argument, NULL, 'a' },
{ "from", required_argument, NULL, 'f' },
{ "sender", required_argument, NULL, 's' },
{ "fromexp", required_argument, NULL, 'E' },
{ "sendmail", no_argument, NULL, 'M' },
{ NULL, 0, NULL, 0 }
};
FILE *pwfile;
opt_date = time (NULL);
opt_expire = getenv ("MACUTIL_EXPIRE");
if (!opt_expire)
opt_expire = "+21D";
opt_pwfile = getenv ("MACUTIL_PASSFILE");
if (!opt_pwfile)
opt_pwfile = default_macpass ();
progname = strrchr (argv[0], '/');
if (progname)
progname++;
else
progname = argv[0];
if (!strncmp (progname, "send", 4))
return do_sendmail (argc, argv);
while ((c = getopt_long (argc, argv, "+Vcf:gp:s:", o, NULL)) != -1)
switch (c) {
case 0:
break;
case 'E':
opt_fromexp = optarg;
break;
case 'M':
return do_sendmail (argc - optind + 1, argv + optind - 1);
case 'a':
opt_aux = optarg;
break;
case 'v':
version (progname, 1);
break;
case 'V':
opt_verbose = 1;
break;
case 'd':
opt_date = parse_expire (optarg, time (NULL));
break;
case 'e':
opt_expire = optarg;
break;
case 'p':
opt_pwfile = optarg;
break;
case 'f':
opt_from = optarg;
break;
case 's':
opt_sender = optarg;
break;
case 'c':
case 'g':
mode = c;
break;
default:
usage ();
break;
}
argv += optind;
argc -= optind;
if (opt_sender) {
if (mode == 'c')
usage ();
mode = 's';
}
if (!mode && opt_from && (opt_sender = getenv ("MACUTIL_SENDER")))
mode = 's';
if (!mode || opt_date == -1)
usage ();
pwfile = fopen (opt_pwfile, "r");
if (!pwfile) {
perror (opt_pwfile);
exit (1);
}
switch (mode) {
case 'g':
{
long exp = parse_expire (opt_expire, opt_date);
char *pw = getmacpw (pwfile);
if (argc || exp == -1)
usage ();
if (!pw) {
fprintf (stderr, "%s: no passwords\n", opt_pwfile);
exit (1);
}
do_gen (pw, exp, opt_aux);
bzero (pw, 20);
break;
}
case 'c':
if (argc != 1)
usage ();
do_check (pwfile, argv[0], opt_aux);
break;
case 's':
{
char *from;
long exp = parse_expire (opt_expire, opt_date);
if (argc || exp == -1)
usage ();
from = mksender (exp, opt_aux);
if (!from)
exit (1);
if (opt_from) {
printf ("%s", opt_from);
if (opt_fromexp || (opt_fromexp = getenv ("MACUTIL_FROMEXP"))) {
time_t t = exp;
char buf[80];
strftime (buf, sizeof (buf), "%d %b %Y", localtime (&t));
buf[sizeof (buf) - 1] = '\0';
printf (" (%s%s%s)", opt_fromexp, *opt_fromexp ? " " : "", buf);
}
printf (" <%s>\n", from);
}
else
printf ("%s\n", from);
exit (0);
break;
}
default:
usage ();
break;
}
#if 0
hmac_sha1 (buf, argv[1], argv[2], strlen (argv[2]));
for (i = 0; i < sizeof (buf); i++)
printf ("%02x", buf[i]);
printf ("\n");
#endif
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1