From: Adrian Hosey <ahosey@sherrill.kiva.net>
Subject: Re: pam_regex
To: pam-list@redhat.com
Date: Fri, 14 Nov 1997 15:27:25 -0500 (EST)

: 
: 
: Chris Dent writes:
: >One of my colleagues <ahosey@kiva.net> at work has put together a
: >pam_regex module which works like pam_listfile (see below).
: 
: Very cool!

Well darn. Chris has gone and done it before I wanted to, like a
mother pushing her child out onto the stage. ("Maaaaaa, I don't WANNA
do this!") We haven't had a chance to even use the module in-house
yet, but I'll submit it for your use and abuse. Comments and
corrections welcome. Thanks!

: >I was considering a slash-delimited pattern a la Perl: pattern=/ah.*/
: >if people are more comfortable with that. (It makes the pattern stand
: >out a little on the command line.) There are two possible drawbacks
: >(1) I'd have to handle escaped slashes in the pattern, and (2) people
: >might start thinking these are Perl regexes, which they aren't.
: 
: Hmm.  I think that using it with something with slashes in it would
: be relatively common (like shells); I'd suggest leaving it like it
: is, without the slashes.
: 

Agreed.


----------------<CUT>------------------

/* 
 * ahosey@kiva.net 
 * Oct 27 1997 
 * from pam_listfile.c by Elliot Lee (His credits below).  
 */
   

/*
 * by Elliot Lee <sopwith@redhat.com>, Red Hat Software.
 * July 25, 1996.
 * This code shamelessly ripped from the pam_rootok module.
 */

#define _SVID_SOURCE
#define _BSD_SOURCE
#define __USE_BSD
#define __USE_SVID
#define __USE_MISC
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <syslog.h>
#include <stdarg.h>
#include <string.h>
#include <pwd.h>
#include <grp.h>
#include <regex.h>

#ifdef DEBUG
#include <assert.h>
#endif

/*
 * here, we make a definition for the externally accessible function
 * in this file (this definition is required for static a module
 * but strongly encouraged generally) it is used to instruct the
 * modules include file to define the function prototypes.
 */

#define PAM_SM_AUTH

#include <security/pam_modules.h>

/* some syslogging */

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

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

/* Our own strdup()-style routine */
static char *xstrdup(const char *x)
{
    char *s;
    
    if ((s = (char *) malloc(strlen(x)+1))) {
	strcpy(s,x);
    }
    
    return s;
}

/* checks if a user is on a list of members */
static int is_on_list(char * const *list, const char *member)
{
    while (*list) {
        if (strcmp(*list, member) == 0)
            return 1;
        list++;
    }
    return 0;
}

/* Checks if a user is a member of a group */
static int is_on_group(const char *user_name, const char *group_name)
{
    struct passwd *pwd;
    struct group *grp, *pgrp;
    char uname[BUFSIZ], gname[BUFSIZ];
    
    if (!strlen(user_name))
        return 0;
    if (!strlen(group_name))
        return 0;
    bzero(uname, sizeof(uname));
    strncpy(uname, user_name, BUFSIZ-1);
    bzero(gname, sizeof(gname));
    strncpy(gname, group_name, BUFSIZ-1);
        
    setpwent();
    pwd = getpwnam(uname);
    endpwent();
    if (!pwd)
        return 0;

    /* the info about this group */
    setgrent();
    grp = getgrnam(gname);
    endgrent();
    if (!grp)
        return 0;
    
    /* first check: is a member of the group_name group ? */
    if (is_on_list(grp->gr_mem, uname))
        return 1;

    /* next check: user primary group is group_name ? */
    setgrent();
    pgrp = getgrgid(pwd->pw_gid);
    endgrent();
    if (!pgrp)
        return 0;
    if (!strcmp(pgrp->gr_name, gname))
        return 1;
        
    return 0;
}

/* --- authentication management functions (only) --- */

/* Extended Items that are not directly available via pam_get_item() */
#define EI_GROUP (1 << 0)
#define EI_SHELL (1 << 1)

/* Constants for apply= parameter */
#define APPLY_TYPE_NULL		0
#define APPLY_TYPE_NONE		1
#define APPLY_TYPE_USER		2
#define APPLY_TYPE_GROUP	3

PAM_EXTERN
int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
    int retval, i, j, citem=0, extitem=0, onerr=PAM_SERVICE_ERR, sense=2;
    const char *citemp;
    char *pattern=NULL;
    char mybuf[256],myval[256];
    char apply_val[256];
    int apply_type;
    regex_t compiled_re;

    /* Stuff for "extended" items */
    struct passwd *userinfo;
    struct group *grpinfo;
    char *itemlist[256]; /* Maximum of 256 items */

    apply_type=APPLY_TYPE_NULL;
    memset(apply_val,0,sizeof(apply_val));

    for(i=0; i < argc; i++) {
	{
	    char *junk;
	    junk = (char *) malloc(strlen(argv[i])+1);
	    if (junk == NULL) {
		return PAM_BUF_ERR;
	    }
	    strcpy(junk,argv[i]);
	    strncpy(mybuf,strtok(junk,"="),255);
	    strncpy(myval,strtok(NULL,"="),255);
	    free(junk);
	}
	if(!strcmp(mybuf,"onerr"))
	    if(!strcmp(myval,"succeed"))
		onerr = PAM_SUCCESS;
	    else if(!strcmp(myval,"fail"))
		onerr = PAM_SERVICE_ERR;
	    else
		return PAM_SERVICE_ERR;
	else if(!strcmp(mybuf,"sense"))
	    if(!strcmp(myval,"allow"))
		sense=0;
	    else if(!strcmp(myval,"deny"))
		sense=1;
	    else
		return onerr;
	else if(!strcmp(mybuf,"pattern")) {
	    pattern = (char *)malloc(strlen(myval)+1);
	    strcpy(pattern,myval);
	} else if(!strcmp(mybuf,"item"))
	    if(!strcmp(myval,"user"))
		citem = PAM_USER;
	    else if(!strcmp(myval,"tty"))
		citem = PAM_TTY;
	    else if(!strcmp(myval,"rhost"))
		citem = PAM_RHOST;
	    else if(!strcmp(myval,"ruser"))
		citem = PAM_RUSER;
	    else { /* These items are related to the user, but are not
		      directly gettable with pam_get_item */
		citem = PAM_USER;
		if(!strcmp(myval,"group"))
		    extitem = EI_GROUP;
		else if(!strcmp(myval,"shell"))
		    extitem = EI_SHELL;
		else
		    citem = 0;
	    } else if(!strcmp(mybuf,"apply")) {
		apply_type=APPLY_TYPE_NONE;
		if (myval[0]=='@') {
		    apply_type=APPLY_TYPE_GROUP;
		    strncpy(apply_val,myval+1,sizeof(apply_val)-1);
		} else {
		    apply_type=APPLY_TYPE_USER;
		    strncpy(apply_val,myval,sizeof(apply_val)-1);
		}
	    } else {
		_pam_log(LOG_ERR,"Unknown option: %s",mybuf);
		return onerr;
	    }
    }

    if(!citem) {
	_pam_log(LOG_ERR,"Unknown item or item not specified");
	return onerr;
    } else if(!pattern) {
	_pam_log(LOG_ERR,"Regular expression not specified");
	return onerr;
    } else if(sense == 2) {
	_pam_log(LOG_ERR,"Unknown sense or sense not specified");
	return onerr;
    } else if(
	      (apply_type==APPLY_TYPE_NONE) || 
	      ((apply_type!=APPLY_TYPE_NULL) && (*apply_val=='\0'))
              ) {
	_pam_log(LOG_ERR,"Invalid usage for apply= parameter");
	return onerr;
    }
     
    /* Check if it makes sense to use the apply= parameter */
    if (apply_type != APPLY_TYPE_NULL) {
	if((citem==PAM_USER) || (citem==PAM_RUSER)) {
	    _pam_log(LOG_WARNING,"Non-sense use for apply= parameter");
	    apply_type=APPLY_TYPE_NULL;
	}
	if(extitem && (extitem==EI_GROUP)) {
	    _pam_log(LOG_WARNING,"Non-sense use for apply= parameter");
	    apply_type=APPLY_TYPE_NULL;
	}
    }
     
    /* Compile the pattern. */
    if(regcomp(&compiled_re, pattern, 0) != 0) {
        _pam_log(LOG_ERR, "Couldn't compile pattern");
	return onerr;
    }

    /* Short-circuit - test if this session apply for this user */
    {
	const char *user_name;
	int rval;
       
	rval=pam_get_user(pamh,&user_name,NULL);
	if((rval==PAM_SUCCESS) && user_name[0]) {
	    /* Got it ? Valid ? */
	    if(apply_type==APPLY_TYPE_USER) {
		if(strcmp(user_name, apply_val)) {
		    /* Does not apply to this user */
#ifdef DEBUG
		    _pam_log(LOG_DEBUG,"don't apply: apply=%s, user=%s",
			     apply_val,user_name);
#endif /* DEBUG */
		    return PAM_IGNORE;
		}
	    } else if(apply_type==APPLY_TYPE_GROUP) {
		if(!is_on_group(user_name,apply_val)) {
		    /* Not a member of apply= group */
#ifdef DEBUG
		    _pam_log(LOG_DEBUG,"don't apply: %s not a member of group %s",
			     user_name,apply_val);
#endif /* DEBUG */
		    return PAM_IGNORE;
		}
	    }
	}
    }

    retval = pam_get_item(pamh,citem,(const void **)&citemp);
    if(retval != PAM_SUCCESS) {
	return onerr;
    }
    if((citem == PAM_USER) && !citemp) {
	pam_get_user(pamh,&citemp,NULL);
	if (retval != PAM_SUCCESS)
	    return PAM_SERVICE_ERR;
    }

    if(!citemp || (strlen(citemp) <= 0)) {
	/* The item was NULL - we are sure not to match */
	return sense?PAM_SUCCESS:PAM_AUTH_ERR;
    }

    if(extitem) {
	switch(extitem) {
	    /* Group checking only checks primary groups (i.e. listed in
	       /etc/passwd) - it doesn't check other ones.  To add full
	       group checking capability would require a redo of things
	       here, since the now-short main comparison loop would have to
	       go through an array of items. */
	    case EI_GROUP:
		setpwent();
		userinfo = getpwnam(citemp);
		setgrent();
		grpinfo = getgrgid(userinfo->pw_gid);
		itemlist[0] = xstrdup(grpinfo->gr_name);
		setgrent();
		for(i=1; (i < 255) && (grpinfo = getgrent()); i++) {
		    for(j=0;grpinfo->gr_mem[j];)
			itemlist[i++] = xstrdup(grpinfo->gr_mem[j++]);
		}
		itemlist[i] = NULL;
		endgrent();
		endpwent();
		break;
	    case EI_SHELL:
		setpwent();
		userinfo = getpwnam(citemp); /* Assume that we have already gotten
						PAM_USER in pam_get_item() - a valid
						assumption since citem gets set to
						PAM_USER in the extitem switch */
		citemp = userinfo->pw_shell;
		endpwent();
		break;
	    default:
		_pam_log(LOG_ERR,"Internal weirdness, unknown extended item %d",
			 extitem);
		return onerr;
	}
    }
#ifdef DEBUG
    _pam_log(LOG_INFO,"Got pattern = %s, item = %d, value = %s, sense = %d",
	     pattern, citem, citemp, sense);
#endif

    /* There should be no more errors from here on */
    retval=PAM_AUTH_ERR;
    /* This loop assumes that PAM_SUCCESS == 0
       and PAM_AUTH_ERR != 0 */
#ifdef DEBUG
    assert(PAM_SUCCESS == 0);
    assert(PAM_AUTH_ERR != 0);
#endif
    if(extitem == EI_GROUP) {
        for(i=0;itemlist[i];i++) {
	    retval = regexec(&compiled_re, itemlist[i], 0, NULL, 0);
	    if(retval)
	      break;	      
	}
	for(i=0;itemlist[i];)
	    free(itemlist[i++]);
    } else {
      retval = regexec(&compiled_re, citemp, 0, NULL, 0);
    }

    if(retval) {
#ifdef DEBUG
	syslog(LOG_INFO,"Returning %d, retval = %d",
	       sense?PAM_AUTH_ERR:PAM_SUCCESS, retval);
#endif
	return sense?PAM_SUCCESS:PAM_AUTH_ERR;
    }
    else {
#ifdef DEBUG
	syslog(LOG_INFO,"Returning %d, retval = %d",
	       sense?PAM_SUCCESS:PAM_AUTH_ERR, retval);
#endif
	return sense?PAM_AUTH_ERR:PAM_SUCCESS;
    }
}

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

#ifdef PAM_STATIC

/* static module data */

struct pam_module _pam_regex_modstruct = {
    "pam_regex",
    pam_sm_authenticate,
    pam_sm_setcred,
    NULL,
    NULL,
    NULL,
    NULL,
};

#endif

/* end of module definition */


-----------------<CUT>------------------

-- 
"Of course 5 years from now that will be different, but 5 years from
now everyone will be running free GNU on their 200 MIPS, 64M
SPARCstation-5." - Andrew Tannenbaum, 1992

-- 
To unsubscribe: mail -s unsubscribe pam-list-request@redhat.com < /dev/null