[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Nightmare : pam_autologin



To me, it has been a glaring, yawning gulf in the lineup of pam modules that
there hasn't been a module to automatically log in a user according to their
IP, or TTY.

I appreciate that there are security issues involved with this, but in my
situation it's necessary to do this to set up single-purpose workstations.
That is, terminals that when turned on, start up and run particular
programs, and logout when that program exits. There are endless practical
applications for this - information booths,  data entry terminals, and so
on, where you want a machine to have a particular single function, and you
particularly care who operates it.  I beilieve that this is important, vital
even, but clearly not many other people feel the same way, or there would
already be a solution available.

So, much as I was reluctant to do this, I've done something myself -
shamelessly cobbled together from various other modules. And guess what ? It
works ! It does what I want it to do. But I thought that I should bounce
this of this mailing list to see if persons more knowledgable than myself
can assist in improving this. I'm aware that the code is rough, in
particular, I was uncertain about how to finish and grant access. As I say,
what is here works, but maybe it can be improved.

The idea is : a configuration file, hard coded as /etc/autologin.conf as
follows:

   machine1.test.net    boris
   ttyS1  natasha

There are two "fields" to the file. The first is either an IP or TTY, the
second entry is the user to be logged in as.


/etc/pam.d/login has as it's first line:

     auth       sufficient   /lib/security/pam_autologin.so


pam_autologin checks that the config file exists, that it is owned by user
and group root, and has permissions of 0600. If so, if either the tty or the
IP matches the first field, the connecting terminal is logged in as the user
in the second field. If pam_autologin logs the user in, this is logged, if
not, control passes silently to the other modules in the /etc/pam.d/login
stack.

The key function is

   static int _pam_check_autologin(pam_handle_t *pamh,
           int flags,
           int argc,
           const char **argv)

Of which there is :

     const char *user;

I'd welcome any constructive comments on how this could be improved, but
there are three issues in particular that I am conscious of:

**** As shown, the config file uses DNS names, following the available item
"retval = pam_get_item (pamh, PAM_RHOST, (const void **)&current);", but
really I would have preferred to use IPs, to avoid the extra step of name
lookups. Anyone know how to do this ?

**** "retval = pam_set_item(pamh, PAM_USER, user);" does not return
PAM_SUCCESS, but appears to set the user in any case. Is there a better way
of doing this ?

**** Oddly, I was also having problems with "user_pwd = getpwnam(user);",
which appeared to be trashing my user pointer. Why it should do this, I have
no idea, there is something flakey and not quite right here, but I can't see
what it is. Any ideas ?


Regards to all,  TimJ.



Source code is as follows - PAM_AUTOLOGIN.C :

#define _BSD_SOURCE

#ifdef linux
#include <endian.h>
#endif

#ifdef NEED_FSUID_H
#include <sys/fsuid.h>
#endif /* NEED_FSUID_H */

#include <sys/types.h>
#include <sys/uio.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>       /* This is supposed(?) to contain the following */
int innetgr(const char *, const char *, const char *,const char *);

#include <stdio.h>
#include <errno.h>
#include <sys/time.h>
#include <arpa/inet.h>

#ifndef MAXDNAME
#define MAXDNAME  256
#endif

#include <stdarg.h>
#include <ctype.h>

#include <net/if.h>
#ifdef linux
# include <linux/sockios.h>
# ifndef __USE_MISC
#  define __USE_MISC
#  include <sys/fsuid.h>
# endif /* __USE_MISC */
#endif

#include <pwd.h>
#include <grp.h>
#include <sys/file.h>
#include <sys/signal.h>
#include <sys/stat.h>
#include <syslog.h>
#ifndef _AUTOLOGIN_CONF
#define _AUTOLOGIN_CONF "/etc/autologin.conf"
#endif /* _AUTOLOGIN_CONF */

#define PAM_SM_AUTH  /* only defines this management group */

#include <security/pam_modules.h>
#include <security/_pam_macros.h>

/* to the best of my knowledge, all modern UNIX boxes have 32 bit integers
*/
#define U32 unsigned int
#define TTY_PREFIX "/dev/"



/* logging */
static void _pam_log(int err, const char *format, ...)
{
    va_list args;

    va_start(args, format);
    openlog("pam_autologin", LOG_CONS|LOG_PID, LOG_AUTH);
    vsyslog(err, format, args);
    va_end(args);
    closelog();
}


/*
 * Obtain the name of the remote host. Currently, this is simply by
 * requesting the contents of the PAM_RHOST item.
 */

static int pam_get_rhost(pam_handle_t *pamh, const char **rhost
    , const char *prompt)
{
    int retval;
    const char *current;

    retval = pam_get_item (pamh, PAM_RHOST, (const void **)&current);
    if (retval != PAM_SUCCESS) return retval;
    if (current == NULL) return PAM_AUTH_ERR;

    *rhost = current;

    return retval;        /* pass on any error from conversation */
}


static int pam_get_tty(pam_handle_t *pamh, const char **utty
    , const char *prompt)
{   
    int retval;
    const char *current;
    
    retval = pam_get_item(pamh, PAM_TTY, (const void **)&current);
    if (retval != PAM_SUCCESS) return retval;
    if (current == NULL) return PAM_AUTH_ERR;
     
    /* The PAM_TTY item may be prefixed with "/dev/" - skip that */
    if (strncmp(TTY_PREFIX, current, sizeof(TTY_PREFIX)-1) == 0)
 current += sizeof(TTY_PREFIX)-1;
    *utty = current;

    return retval;        /* pass on any error from conversation */
}


/*
 * Returns 1 for blank lines (or only comment lines) and 0 otherwise
 */

static int __isempty(char *p)
{
    while (*p && isspace(*p)) {
 ++p;
    }

    return (*p == '\0' || *p == '#') ? 1:0 ;
}



static 
int check_cfg_sec(const char *pathname)
/* check that a configuration file is secure */
{
    struct stat statbuf;

    int retval = 0;

/* Config File must be :
* Owned by User Root, Group Root
* Have perms 0600
* Not be a Symbolic Lynk */

    if (lstat(pathname, &statbuf) < 0) {
        _pam_log(LOG_WARNING, "Configuration File Not Available");
        retval = 1;
    }
    else {
 if ((statbuf.st_mode & S_IFLNK) == S_IFLNK)
 {
     _pam_log(LOG_WARNING, "Configuration File is Symbolic Link");
     retval = 1;
 }

 if (statbuf.st_mode & ~(S_IFREG | S_IREAD | S_IWRITE))
 {
     _pam_log(LOG_WARNING, "Configuration File must have no more than 0600
permissions");
     retval = 1;
       }

 if (statbuf.st_uid != 0)
 if (statbuf.st_gid != 0)
 {
     _pam_log(LOG_WARNING, "Configuration File is Symbolic Link");
     retval = 1;
      }
    }
    return(retval);
}


static int read_autologin_conf(const char **rhost, const char **utty, const
char **user)
{
    register char *p;
    FILE *hostf;
    int fndflag;
/*    char *user;*/
    char buf[MAXHOSTNAMELEN + 128];             /* host + login */

    buf[sizeof (buf)-1] = '\0';                 /* terminate line */

    if (check_cfg_sec(_AUTOLOGIN_CONF) == 0) {
 hostf = fopen (_AUTOLOGIN_CONF, "r");
 if (hostf) {
     while (fgets(buf, sizeof(buf), hostf) != NULL)


  /* hostf file line */
  p = buf;

  /* Skip empty or comment lines */
  if (__isempty(p)) {
      continue;
  }

  /* Skip lines that are too long. */
  if (strchr(p, '\n') == NULL) {
      int ch = getc(hostf);

      while (ch != '\n' && ch != EOF)
   ch = getc(hostf);
      continue;
  }

  fndflag=1;
  if (rhost != NULL)  fndflag=strncmp(buf, *rhost, sizeof(rhost));
  if (fndflag != 0) if (utty  != NULL)  fndflag =
        strncmp(buf, *utty, sizeof(utty));
  if (fndflag == 0) {
      for (;*p && !isspace(*p); ++p);
      /* <nul> terminate hostname and skip spaces */
      for (*p++='\0'; *p && isspace(*p); ++p);

      *user = p;       /* this is the user's name */

     printf("\n1. Name found by read_autologin_conf = %s \n", p);
     getchar();
      while (*p && !isspace(*p)) ++p;/* find end of user's name */

      *p = '\0';        /* <nul> terminate username */
      return (1);
  }
     }
 }
    }
    return (0);
}



/*
 * Internal function to do authentication
 */

static int _pam_check_autologin(pam_handle_t *pamh,
        int flags,
        int argc,
        const char **argv)
{
    int retval ;
    const char *rhost, *utty, *user;
    struct passwd *user_pwd;

    /* get the remotehost */
    retval= pam_get_rhost(pamh, &rhost, NULL);
    retval = pam_get_tty(pamh, &utty, NULL);

    if (read_autologin_conf(&rhost, &utty, &user))
    {
        user_pwd = getpwnam(user);
        if (user_pwd == NULL) {
            _pam_log(LOG_WARNING, "user '%s' unknown to this system", user);
           retval = PAM_AUTH_ERR;
       } else {
           retval = pam_set_item(pamh, PAM_USER, user);
           _pam_log(LOG_WARNING, "Access granted to user '%s'" , user);
           return PAM_SUCCESS;
 }
    }
    return PAM_AUTH_ERR;
}

/* --- authentication management functions --- */

PAM_EXTERN
int pam_sm_authenticate (pam_handle_t *pamh,
    int flags,
    int argc,
    const char **argv)
{
    int retval;

    if (sizeof(U32) != 4) {
       _pam_log (LOG_ALERT, "pam_autologin module can\'t work on this
hardware "
    "(yet)");
       return PAM_AUTH_ERR;
    }
    sethostent(1);
    retval = _pam_check_autologin(pamh, flags, argc, argv);
    endhostent();
    return retval;
}


PAM_EXTERN
int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc,
     const char **argv)
{
    return PAM_SUCCESS;
}

/* end of module definition */


#ifdef PAM_STATIC

/* static module data */

struct pam_module _pam_rhosts_auth_modstruct = {
    "pam_rhosts_auth",
    pam_sm_authenticate,
    pam_sm_setcred,
    NULL,
    NULL,
    NULL,
    NULL,
};

#endif










[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index] []