Logo Search packages:      
Sourcecode: mailutils version File versions

popauth.c

/* GNU Mailutils -- a suite of utilities for electronic mail
   Copyright (C) 1999, 2000, 2001, 2002, 2005 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 2, 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, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA  */

#include "pop3d.h"
#include <mailutils/argcv.h>
#include <xalloc.h>

int db_list (char *input_name, char *output_name);
int db_make (char *input_name, char *output_name);

#define ACT_CREATE  0
#define ACT_ADD     1
#define ACT_DELETE  2
#define ACT_LIST    3
#define ACT_CHPASS  4

static int permissions = 0600;

struct action_data {
  int action;
  char *input_name;
  char *output_name;
  char *username;
  char *passwd;
};

void check_action(int action);
int action_create __P((struct action_data *ap));
int action_add __P((struct action_data *ap));
int action_delete __P((struct action_data *ap));
int action_list __P((struct action_data *ap));
int action_chpass __P((struct action_data *ap));

int (*ftab[]) __P((struct action_data *)) = {
  action_create,
  action_add,
  action_delete,
  action_list,
  action_chpass
};

const char *program_version = "popauth (" PACKAGE_STRING ")";
static char doc[] = N_("GNU popauth -- manage pop3 authentication database");
static error_t popauth_parse_opt  __P((int key, char *arg,
                               struct argp_state *astate));

void popauth_version __P((FILE *stream, struct argp_state *state));
void (*argp_program_version_hook) __P((FILE *stream,
                               struct argp_state *state)) =
                                   popauth_version;

static struct argp_option options[] = 
{
  { NULL, 0, NULL, 0, N_("Actions are:"), 1 },
  { "add", 'a', 0, 0, N_("Add user"), 1 },
  { "modify", 'm', 0, 0, N_("Modify user's record (change password)"), 1 },
  { "delete", 'd', 0, 0, N_("Delete user's record"), 1 },
  { "list", 'l', 0, 0, N_("List the contents of DBM file"), 1 },
  { "create", 'c', 0, 0, N_("Create the DBM from a plaintext file"), 1 },

  { NULL, 0, NULL, 0,
    N_("Default action is:\n"
    "  For the file owner: --list\n"
    "  For a user: --modify --username <username>\n"), 2 },
  
  { NULL, 0, NULL, 0, N_("Options are:"), 3 },
  { "file", 'f', N_("FILE"), 0, N_("Read input from FILE (default stdin)"), 3 },
  { "output", 'o', N_("FILE"), 0, N_("Direct output to file"), 3 },
  { "password", 'p', N_("STRING"), 0, N_("Specify user's password"), 3 },
  { "user", 'u', N_("USERNAME"), 0, N_("Specify user name"), 3 },
  { "permissions", 'P', N_("PERM"), 0, N_("Force given permissions on the database"), 3 },
  { NULL, }
};

static struct argp argp = {
  options,
  popauth_parse_opt,
  NULL,
  doc,
  NULL,
  NULL, NULL
};

static const char *popauth_argp_capa[] = {
  "common",
  "license",
  NULL
};

static void
set_db_perms (struct argp_state *astate, char *opt, int *pperm)
{
  int perm = 0;
   
  if (isdigit(opt[0]))
    {
      char *p;
      perm = strtoul (opt, &p, 8);
      if (*p)
      {
        argp_error (astate, _("Invalid octal number: %s"), opt);
        exit (1);
      }
    }
  *pperm = perm;
}

static error_t
popauth_parse_opt (int key, char *arg, struct argp_state *astate)
{
  struct action_data *ap = astate->input;
  switch (key)
    {
    case ARGP_KEY_INIT:
      memset (ap, 0, sizeof(*ap));
      ap->action = -1;
      break;
      
    case 'a':
      check_action (ap->action);
      ap->action = ACT_ADD;
      break;

    case 'c':
      check_action (ap->action);
      ap->action = ACT_CREATE;
      break;
      
    case 'l':
      check_action (ap->action);
      ap->action = ACT_LIST;
      break;
      
    case 'd':
      check_action (ap->action);
      ap->action = ACT_DELETE;
      break;
        
    case 'p':
      ap->passwd = arg;
      break;
      
    case 'm':
      check_action (ap->action);
      ap->action = ACT_CHPASS;
      break;
      
    case 'f':
      ap->input_name = arg;
      break;
        
    case 'o':
      ap->output_name = arg;
      break;
      
    case 'u':
      ap->username = arg;
      break;
      
    case 'P':
      set_db_perms (astate, arg, &permissions);
      break;
      
    case ARGP_KEY_FINI:
      if (ap->action == -1)
      {
        /* Deduce the default action */
        if (getuid () == 0)
          ap->action = ACT_LIST;
        else
          ap->action = ACT_CHPASS;
      }
      break;

    default:
      return ARGP_ERR_UNKNOWN;
    }
  return 0;
}

int
main(int argc, char **argv)
{
  struct action_data adata;

  /* Native Language Support */
  mu_init_nls ();

  mu_argp_init (program_version, NULL);
  mu_argp_parse (&argp, &argc, &argv, 0,
             popauth_argp_capa, NULL, &adata);

  return (*ftab[adata.action]) (&adata);
}

void
check_action (int action)
{
  if (action != -1)
    {
      mu_error (_("You may not specify more than one `-aldp' option"));
      exit (1);
    }
}

int
check_user_perm (int action, struct action_data *ap)
{
  struct stat sb;
  struct passwd *pw;
  uid_t uid;
  
  if (!ap->input_name)
    ap->input_name = APOP_PASSFILE;

  if (mu_dbm_stat (ap->input_name, &sb))
    {
      if (ap->action == ACT_ADD)
      {
        DBM_FILE db;
        if (mu_dbm_open (ap->input_name, &db, MU_STREAM_CREAT, permissions))
          {
            mu_error (_("Cannot create %s: %s"),
                  ap->input_name, mu_strerror (errno));
            exit (1);
          }
        mu_dbm_close (db);
        mu_dbm_stat (ap->input_name, &sb);
      }
      else
      {
        mu_error (_("Cannot stat %s: %s"), ap->input_name, mu_strerror (errno));
        exit (1);
      }
    }

  uid = getuid ();
  if (uid == 0 || sb.st_uid == uid)
    return 0;

  if (ap->username)
    {
      mu_error (_("Only the file owner can use --username"));
      exit (1);
    }

  if (action != ACT_CHPASS)
    {
      mu_error (_("Operation not allowed"));
      exit (1);
    }
  pw = getpwuid (uid);
  if (!pw)
    exit (1);
  ap->username = pw->pw_name;
  return 1;
}

int
action_list (struct action_data *ap)
{
  FILE *fp;
  DBM_FILE db;
  DBM_DATUM key;
  DBM_DATUM contents;
  
  check_user_perm (ACT_LIST, ap);
  if (mu_dbm_open (ap->input_name, &db, MU_STREAM_READ, permissions))
    {
      mu_error (_("Cannot open %s: %s"), ap->input_name, mu_strerror (errno));
      return 1;
    }
  
  if (ap->output_name)
    {
      fp = fopen (ap->output_name, "w");
      if (!fp)
      {
        mu_error (_("Cannot create %s: %s"), ap->output_name, mu_strerror (errno));
        return 1;
      }
    }
  else
    fp = stdout;

  if (ap->username)
    {
      memset (&key, 0, sizeof key);
      memset (&contents, 0, sizeof contents);
      MU_DATUM_PTR (key) = ap->username;
      MU_DATUM_SIZE (key) = strlen (ap->username);
      if (mu_dbm_fetch (db, key, &contents))
      {
        mu_error (_("No such user: %s"), ap->username);
      }
      else
      fprintf (fp, "%.*s: %.*s\n",
             (int) MU_DATUM_SIZE (key),
             (char*) MU_DATUM_PTR (key),
             (int) MU_DATUM_SIZE (contents),
             (char*) MU_DATUM_PTR (contents));
    }
  else
    {
      for (key = mu_dbm_firstkey (db); MU_DATUM_PTR(key);
         key = mu_dbm_nextkey (db, key))
      {
        memset (&contents, 0, sizeof contents);
        mu_dbm_fetch (db, key, &contents);
        fprintf (fp, "%.*s: %.*s\n",
               (int) MU_DATUM_SIZE (key),
               (char*) MU_DATUM_PTR (key),
               (int) MU_DATUM_SIZE (contents),
               (char*) MU_DATUM_PTR (contents));
      }
    }
  
  mu_dbm_close (db);
  fclose (fp);
  return 0;
}

int
action_create (struct action_data *ap)
{
  FILE *fp;
  DBM_FILE db;
  DBM_DATUM key;
  DBM_DATUM contents;
  char buf[256];
  int line = 0;

  /* Make sure we have proper privileges if popauth is setuid */
  setuid (getuid ());
  
  if (ap->input_name)
    {
      fp = fopen (ap->input_name, "r");
      if (!fp)
      {
        mu_error (_("Cannot open %s: %s"), ap->input_name, mu_strerror (errno));
        return 1;
      }
    }
  else
    {
      ap->input_name = "";
      fp = stdin;
    }
  
  if (!ap->output_name)
    ap->output_name = APOP_PASSFILE;
  if (mu_dbm_open (ap->output_name, &db, MU_STREAM_CREAT, permissions))
    {
      mu_error (_("Cannot create %s: %s"), ap->output_name, mu_strerror (errno));
      return 1;
    }

  line = 0;
  while (fgets (buf, sizeof buf - 1, fp))
    {
      int len;
      int argc;
      char **argv;

      len = strlen (buf);
      if (buf[len-1] == '\n')
      buf[--len] = 0;
      
      line++;
      if (argcv_get (buf, ":", NULL, &argc, &argv))
      {
        argcv_free (argc, argv);
        continue;
      }

      if (argc == 0 || argv[0][0] == '#')
      {
        argcv_free (argc, argv);
        continue;
      }
      
      if (argc != 3 || argv[1][0] != ':' || argv[1][1] != 0)
      {
        mu_error (_("%s:%d: malformed line"), ap->input_name, line);
        argcv_free (argc, argv);
        continue;
      }

      memset (&key, 0, sizeof key);
      memset (&contents, 0, sizeof contents);
      MU_DATUM_PTR (key) = argv[0];
      MU_DATUM_SIZE (key) = strlen (argv[0]);
      MU_DATUM_PTR (contents) = argv[2];
      MU_DATUM_SIZE (contents) = strlen (argv[2]);

      if (mu_dbm_insert (db, key, contents, 1))
      mu_error (_("%s:%d: cannot store datum"), ap->input_name, line);

      argcv_free (argc, argv);
    }
  mu_dbm_close (db);
  fclose (fp);
  return 0;
}

int
open_io (int action, struct action_data *ap, DBM_FILE *db, int *not_owner)
{
  int rc = check_user_perm (action, ap);
  if (not_owner)
    *not_owner = rc;
  if (mu_dbm_open (ap->input_name, db, MU_STREAM_RDWR, permissions))
    {
      mu_error (_("Cannot open %s: %s"), ap->input_name, mu_strerror (errno));
      return 1;
    }
  return 0;
}

void
fill_pass (struct action_data *ap)
{
  if (!ap->passwd)
    {
      char *p;

      while (1) {
      if (ap->passwd)
        free (ap->passwd);
      p = getpass (_("Password:"));
      if (!p)
        exit (1);
      ap->passwd = strdup (p);
      /* TRANSLATORS: Please try to format this string so that it has
         the same length as the translation of 'Password:' above */
      p = getpass (_("Confirm :"));
      if (strcmp (ap->passwd, p) == 0)
        break;
      mu_error (_("Passwords differ. Please retry."));
      } 
    }
}

int
action_add (struct action_data *ap)
{
  DBM_FILE db;
  DBM_DATUM key;
  DBM_DATUM contents;
  int rc;
  
  if (!ap->username)
    {
      mu_error (_("Missing username to add"));
      return 1;
    }

  if (open_io (ACT_ADD, ap, &db, NULL))
    return 1;

  fill_pass (ap);
  
  memset (&key, 0, sizeof key);
  memset (&contents, 0, sizeof contents);
  MU_DATUM_PTR (key) = ap->username;
  MU_DATUM_SIZE (key) = strlen (ap->username);
  MU_DATUM_PTR (contents) = ap->passwd;
  MU_DATUM_SIZE (contents) = strlen (ap->passwd);

  rc = mu_dbm_insert (db, key, contents, 1);
  if (rc)
    mu_error (_("Cannot store datum"));

  mu_dbm_close (db);
  return rc;
}

int
action_delete (struct action_data *ap)
{
  DBM_FILE db;
  DBM_DATUM key;
  int rc;
  
  if (!ap->username)
    {
      mu_error (_("Missing username to delete"));
      return 1;
    }

  if (open_io (ACT_DELETE, ap, &db, NULL))
    return 1;
  
  MU_DATUM_PTR (key) = ap->username;
  MU_DATUM_SIZE (key) = strlen (ap->username);

  rc = mu_dbm_delete (db, key);
  if (rc)
    mu_error (_("Cannot remove record for %s"), ap->username);

  mu_dbm_close (db);
  return rc;
}

int
action_chpass (struct action_data *ap)
{
  DBM_FILE db;
  DBM_DATUM key;
  DBM_DATUM contents;
  int rc;
  int not_owner;
  
  if (open_io (ACT_CHPASS, ap, &db, &not_owner))
    return 1;

  if (!ap->username)
    {
      mu_error (_("Missing username"));
      return 1;
    }

  memset (&key, 0, sizeof key);
  memset (&contents, 0, sizeof contents);

  MU_DATUM_PTR (key) = ap->username;
  MU_DATUM_SIZE (key) = strlen (ap->username);
  if (mu_dbm_fetch (db, key, &contents))
    {
      mu_error (_("No such user: %s"), ap->username);
      return 1;
    }

  if (not_owner)
    {
      char *oldpass, *p;
      
      oldpass = xmalloc (MU_DATUM_SIZE (contents) + 1);
      memcpy (oldpass, MU_DATUM_PTR (contents), MU_DATUM_SIZE (contents));
      oldpass[MU_DATUM_SIZE (contents)] = 0;
      p = getpass (_("Old Password:"));
      if (!p)
      return 1;
      if (strcmp (oldpass, p))
      {
        mu_error (_("Sorry"));
        return 1;
      }
    }

  fill_pass (ap);
  
  MU_DATUM_PTR (contents) = ap->passwd;
  MU_DATUM_SIZE (contents) = strlen (ap->passwd);
  rc = mu_dbm_insert (db, key, contents, 1);
  if (rc)
    mu_error (_("Cannot replace datum"));

  mu_dbm_close (db);
  return rc;
}

void
popauth_version (FILE *stream, struct argp_state *state)
{
#if defined(WITH_GDBM)
# define FORMAT "GDBM"
#elif defined(WITH_BDB2)
# define FORMAT "Berkeley DB"  
#elif defined(WITH_NDBM)
# define FORMAT "NDBM"  
#elif defined(WITH_OLD_DBM)
# define FORMAT "Old DBM"  
#endif
  printf ("%s\n", argp_program_version);
  printf (_("Database format: %s\n"), FORMAT);
  printf (_("Database location: %s\n"), APOP_PASSFILE);
  exit (EXIT_SUCCESS);
}

Generated by  Doxygen 1.6.0   Back to index