head	1.7;
access;
symbols;
locks; strict;
comment	@# @;


1.7
date	97.04.05.06.32.06;	author morgan;	state Exp;
branches;
next	1.6;

1.6
date	97.02.15.17.27.20;	author morgan;	state Exp;
branches;
next	1.5;

1.5
date	96.12.01.03.05.54;	author morgan;	state Exp;
branches;
next	1.4;

1.4
date	96.11.10.21.06.07;	author morgan;	state Exp;
branches;
next	1.3;

1.3
date	96.09.05.06.49.36;	author morgan;	state Exp;
branches;
next	1.2;

1.2
date	96.09.01.01.18.40;	author morgan;	state Exp;
branches;
next	1.1;

1.1
date	96.08.29.13.27.51;	author morgan;	state Exp;
branches;
next	;


desc
@@


1.7
log
@new option and also deleted _readto
@
text
@/* 
 * $Id: support.-c,v 1.6 1997/02/15 17:27:20 morgan Exp morgan $
 *
 * $Log: support.-c,v $
 * Revision 1.6  1997/02/15 17:27:20  morgan
 * added helper binary to password checking
 *
 * Revision 1.5  1996/12/01 03:05:54  morgan
 * debugging with _pam_macros.h
 *
 * Revision 1.4  1996/11/10 21:06:07  morgan
 * pwdb conversion
 *
 * Copyright information at end of file.
 */

/*
 * here is the string to inform the user that the new passwords they
 * typed were not the same.
 */

#define MISTYPED_PASS "Sorry, passwords do not match"

/* type definition for the control options */

typedef struct {
     const char *token;
     unsigned int mask;            /* shall assume 32 bits of flags */
     unsigned int flag;
} UNIX_Ctrls;

/*
 * macro to determine if a given flag is on
 */

#define on(x,ctrl)  (unix_args[x].flag & ctrl)

/*
 * macro to determine that a given flag is NOT on
 */

#define off(x,ctrl) (!on(x,ctrl))

/*
 * macro to turn on/off a ctrl flag manually
 */

#define set(x,ctrl)   (ctrl = ((ctrl)&unix_args[x].mask)|unix_args[x].flag)
#define unset(x,ctrl) (ctrl &= ~(unix_args[x].flag))

/* the generic mask */

#define _ALL_ON_  (~0U)

/* end of macro definitions definitions for the control flags */

/* ****************************************************************** *
 * ctrl flags proper..
 */

/*
 * here are the various options recognized by the unix module. They
 * are enumerated here and then defined below. Internal arguments are
 * given NULL tokens.
 */

#define UNIX__OLD_PASSWD          0     /* internal */
#define UNIX__VERIFY_PASSWD       1     /* internal */
#define UNIX__IAMROOT             2     /* internal */

#define UNIX_AUDIT                3     /* print more things than debug..
					   some information may be sensitive */
#define UNIX_USE_FIRST_PASS       4
#define UNIX_TRY_FIRST_PASS       5
#define UNIX_NOT_SET_PASS         6     /* don't set the AUTHTOK items */

#define UNIX__PRELIM              7     /* internal */
#define UNIX__UPDATE              8     /* internal */
#define UNIX__NONULL              9     /* internal */
#define UNIX__QUIET              10     /* internal */
#define UNIX_USE_AUTHTOK         11     /* insist on reading PAM_AUTHTOK */
#define UNIX_SHADOW              12     /* signal shadow on */
#define UNIX_MD5_PASS            13     /* force the use of MD5 passwords */
#define UNIX__NULLOK             14     /* Null token ok */
#define UNIX_RADIUS              15     /* wish to use RADIUS for password */
#define UNIX__SET_DB             16     /* internal - signals redirect to db */
#define UNIX_DEBUG               17     /* send more info to syslog(3) */
#define UNIX_NODELAY             18     /* admin does not want a fail-delay */

/* -------------- */
#define UNIX_CTRLS_              19     /* number of ctrl arguments defined */


static const UNIX_Ctrls unix_args[UNIX_CTRLS_] = {
/* symbol                 token name          ctrl mask      ctrl       *
 * ------------------     ------------------  -------------- ---------- */

/* UNIX__OLD_PASSWD */    {  NULL,            _ALL_ON_,              01 },
/* UNIX__VERIFY_PASSWD */ {  NULL,            _ALL_ON_,              02 },
/* UNIX__IAMROOT */       {  NULL,            _ALL_ON_,              04 },
/* UNIX_AUDIT */          { "audit",          _ALL_ON_,             010 },
/* UNIX_USE_FIRST_PASS */ { "use_first_pass", _ALL_ON_^(060),       020 },
/* UNIX_TRY_FIRST_PASS */ { "try_first_pass", _ALL_ON_^(060),       040 },
/* UNIX_NOT_SET_PASS */   { "not_set_pass",   _ALL_ON_,            0100 },
/* UNIX__PRELIM */        {  NULL,            _ALL_ON_^(0600),     0200 },
/* UNIX__UPDATE */        {  NULL,            _ALL_ON_^(0600),     0400 },
/* UNIX__NONULL */        {  NULL,            _ALL_ON_,           01000 },
/* UNIX__QUIET */         {  NULL,            _ALL_ON_,           02000 },
/* UNIX_USE_AUTHTOK */    { "use_authtok",    _ALL_ON_,           04000 },
/* UNIX_SHADOW */         { "shadow",         _ALL_ON_,          010000 },
/* UNIX_MD5_PASS */       { "md5",            _ALL_ON_,          020000 },
/* UNIX__NULLOK */        { "nullok",         _ALL_ON_^(01000),       0 },
/* UNIX_RADIUS */	  { "radius",         _ALL_ON_,          040000 },
/* UNIX__SET_DB */	  {  NULL,            _ALL_ON_,         0100000 },
/* UNIX_DEBUG */          { "debug",          _ALL_ON_,         0200000 },
/* UNIX_NODELAY */        { "nodelay",        _ALL_ON_,         0400000 },
};

#define UNIX_DEFAULTS  (unix_args[UNIX__NONULL].flag)

/* syslogging function for errors and other information */

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

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

/* this is a front-end for module-application conversations */

static int converse(pam_handle_t *pamh, int ctrl, int nargs
		    , struct pam_message **message
		    , struct pam_response **response)
{
    int retval;
    struct pam_conv *conv;

    D(("begin to converse"));

    retval = pam_get_item( pamh, PAM_CONV, (const void **) &conv ) ; 
    if ( retval == PAM_SUCCESS ) {

	retval = conv->conv(nargs, ( const struct pam_message ** ) message
			    , response, conv->appdata_ptr);

	D(("returned from application's conversation function"));

	if (retval != PAM_SUCCESS && on(UNIX_DEBUG,ctrl) ) {
	    _log_err(LOG_DEBUG, "conversation failure [%s]"
		     , pam_strerror(retval));
	}

    } else {
	_log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
		 , pam_strerror(retval));
    }

    D(("ready to return from module conversation"));

    return retval;                  /* propagate error status */
}

static int make_remark(pam_handle_t *pamh, unsigned int ctrl
		       , int type, const char *text)
{
    if ( off(UNIX__QUIET, ctrl) ) {
	struct pam_message *pmsg[1], msg[1];
	struct pam_response *resp;
	  
	pmsg[0] = &msg[0];
	msg[0].msg = text;
	msg[0].msg_style = type;
	resp = NULL;

	return converse(pamh, ctrl, 1, pmsg, &resp);
    }
    return PAM_SUCCESS;
}

/*
 * set the control flags for the UNIX module.
 */

static int set_ctrl(int flags, int argc, const char **argv)
{
    unsigned int ctrl;

    D(("called."));

    ctrl = UNIX_DEFAULTS;         /* the default selection of options */

    /* set some flags manually */

    if ( getuid() == 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK) ) {
	set(UNIX__IAMROOT, ctrl);
    }
    if ( flags & PAM_UPDATE_AUTHTOK ) {
	set(UNIX__UPDATE, ctrl);
    }
    if ( flags & PAM_PRELIM_CHECK ) {
	set(UNIX__PRELIM, ctrl);
    }
    if ( flags & PAM_DISALLOW_NULL_AUTHTOK ) {
	set(UNIX__NONULL, ctrl);
    }
    if ( flags & PAM_SILENT ) {
	set(UNIX__QUIET, ctrl);
    }

    /* now parse the arguments to this module */

    while (argc-- > 0) {
	int j;

	D(("pam_pwdb arg: %s",*argv));

	for (j=0; j<UNIX_CTRLS_; ++j) {
	    if (unix_args[j].token
		&& ! strcmp(*argv, unix_args[j].token) ) {
		break;
	    }
	}

	if ( j >= UNIX_CTRLS_ ) {
	    _log_err(LOG_ERR, "unrecognized option [%s]",*argv);
	} else {
	    ctrl &= unix_args[j].mask;    /* for turning things off */
	    ctrl |= unix_args[j].flag;    /* for turning things on  */
	}

	++argv;                            /* step to next argument */
    }

    /* these are used for updating passwords */

    if (on(UNIX_SHADOW,ctrl) || on(UNIX_RADIUS,ctrl)) {
	set(UNIX__SET_DB, ctrl);
    }

    /* auditing is a more sensitive version of debug */

    if ( on(UNIX_AUDIT,ctrl) ) {
	set(UNIX_DEBUG, ctrl);
    }

    /* return the set of flags */

    return ctrl;
}

/* use this to free strings. ESPECIALLY password strings */

static char *_pam_delete(register char *xx)
{
    _pam_overwrite(xx);
    _pam_drop(xx);
    return NULL;
}

static void _cleanup(pam_handle_t *pamh, void *x, int error_status)
{
    x = _pam_delete( (char *) x );
}

/*
 * Safe duplication of character strings. "Paranoid"; don't leave
 * evidence of old token around for later stack analysis.
 */

static char *xstrdup(const char *x)
{
    register char *new=NULL;

    if (x != NULL) {
	register int i;

	for (i=0; x[i]; ++i);                       /* length of string */
	if ((new = malloc(++i)) == NULL) {
	    i = 0;
	    _log_err(LOG_CRIT, "out of memory in xstrdup");
	} else {
	    while (i-- > 0) {
		new[i] = x[i];
	    }
	}
	x = NULL;
    }

    return new;                 /* return the duplicate or NULL on error */
}

/* ************************************************************** *
 * Useful non-trivial functions                                   *
 * ************************************************************** */

#include "pam_unix_md.-c"

/*
 * the following is used to keep track of the number of times a user fails
 * to authenticate themself.
 */

#define FAIL_PREFIX                   "-UN*X-FAIL-"
#define UNIX_MAX_RETRIES              3

struct _pam_failed_auth {
    char *user;                  /* user that's failed to be authenticated */
    char *name;                  /* attempt from user with name */
    int id;                      /* uid of name'd user */
    int count;                   /* number of failures so far */
};

#ifndef PAM_DATA_REPLACE
#error "Need to get an updated libpam 0.52 or better"
#endif

static void _cleanup_failures(pam_handle_t *pamh, void *fl, int err)
{
    int quiet;
    const char *service=NULL;
    struct _pam_failed_auth *failure;

    D(("called"));

    quiet = err & PAM_DATA_SILENT;     /* should we log something? */
    err  &= PAM_DATA_REPLACE;          /* are we just replacing data? */
    failure = (struct _pam_failed_auth *) fl;

    if ( failure != NULL ) {

	if ( !quiet && !err ) {   /* under advisement from Sun,may go away */

	    /* log the number of authentication failures */
	    if ( failure->count != 0 ) {
		(void) pam_get_item(pamh, PAM_SERVICE
				    , (const void **)&service);
		_log_err(LOG_NOTICE
			 , "%d authentication failure%s; %s(uid=%d) -> "
			 "%s for %s service"
			 , failure->count, failure->count==1 ? "":"s"
			 , failure->name
			 , failure->id
			 , failure->user
			 , service == NULL ? "**unknown**":service
		    );
		if ( failure->count > UNIX_MAX_RETRIES ) {
		    _log_err(LOG_ALERT
			     , "service(%s) ignoring max retries; %d > %d"
			     , service == NULL ? "**unknown**":service
			     , failure->count
			     , UNIX_MAX_RETRIES );
		}
	    }
	}
	failure->user = _pam_delete(failure->user);            /* tidy up */
	failure->name = _pam_delete(failure->name);            /* tidy up */
	free(failure);
    }
}

/*
 * verify the password of a user
 */

#include <sys/types.h>
#include <sys/wait.h>

static int pwdb_run_helper_binary(pam_handle_t *pamh, const char *passwd)
{
    int retval, child, fds[2];
    
    /* create a pipe for the password */
    if (pipe(fds) != 0) {
	D(("could not make pipe"));
	return PAM_AUTH_ERR;
    }

    /* fork */
    child = fork();
    if (child == 0) {
	static char *args[] = { NULL, NULL };
	static char *envp[] = { NULL };

	/* XXX - should really tidy up PAM here too */
	while (pwdb_end() == PWDB_SUCCESS);

	/* reopen stdin as pipe */
	close(fds[1]);
	dup2(fds[0], STDIN_FILENO);

	/* exec binary helper */
	args[0] = xstrdup(CHKPWD_HELPER);
	execve(CHKPWD_HELPER, args, envp);

	/* should not get here: exit with error */
	exit(PWDB_SUCCESS+1);
    } else if (child > 0) {
	/* wait for child */
	close(fds[0]);
	if (passwd != NULL) {            /* send the password to the child */
	    write(fds[1], passwd, strlen(passwd)+1);
	    passwd = NULL;
	} else {
	    write(fds[1], "", 1);                        /* blank password */
	}
	close(fds[1]);
	(void) waitpid(child, &retval, 0);  /* wait for helper to complete */
	retval = (retval == PWDB_SUCCESS) ? PAM_SUCCESS:PAM_AUTH_ERR;
    } else {
	D(("fork failed"));
	retval = PAM_AUTH_ERR;
    }
    return retval;
}

static int _unix_verify_password(pam_handle_t *pamh, const char *name
				 , const char *p, unsigned int ctrl)
{
    const struct pwdb *pw=NULL;
    const struct pwdb_entry *pwe=NULL;

    const char *salt;
    char *pp;
    char *data_name;
    int retval;

    D(("called"));

#ifdef HAVE_PAM_FAIL_DELAY
    if ( off(UNIX_NODELAY, ctrl) ) {
	D(("setting delay"));
	(void) pam_fail_delay(pamh, 1000000);  /* 1 sec delay for on failure */
    }
#endif

    /* locate the entry for this user */
    
    retval = pwdb_locate("user", PWDB_DEFAULT, name, PWDB_ID_UNKNOWN, &pw);
    if (retval == PWDB_PASS_PHRASE_REQD) {
	/*
	 * give the password to the pwdb library. It may be needed to
	 * access the database
	 */

	retval = pwdb_set_entry( pw, "pass_phrase", p, 1+strlen(p)
				 , NULL, NULL, 0);
	if (retval != PWDB_SUCCESS) {
	    _log_err(LOG_ALERT, "find pass; %s", pwdb_strerror(retval));
	    (void) pwdb_delete(&pw);
	    p = NULL;
	    return PAM_CRED_INSUFFICIENT;
	}

	retval = pwdb_locate("user", pw->source, name, PWDB_ID_UNKNOWN, &pw);
    }

    if (retval != PWDB_SUCCESS) {
	if ( on(UNIX_AUDIT, ctrl) ) {
	    /* this might be a typo and the user has given a password
	       instead of a username. Careful with this. */
	    _log_err(LOG_ALERT, "check pass; user (%s) unknown", name);
	} else {
	    _log_err(LOG_ALERT, "check pass; user unknown");
	}
	(void) pwdb_delete(&pw);
	p = NULL;
	return PAM_USER_UNKNOWN;
    }

    /*
     * courtesy of PWDB the password for the user is stored in
     * encrypted form in the "passwd" entry of pw.
     */

    retval = pwdb_get_entry(pw, "passwd", &pwe);
    if (retval != PWDB_SUCCESS) {
	if (geteuid()) {
	    /* we are not root perhaps this is the reason? Run helper */
	    retval = pwdb_run_helper_binary(pamh, p);
	} else {
	    retval = PAM_AUTHINFO_UNAVAIL;
	    _log_err(LOG_ALERT, "get passwd; %s", pwdb_strerror(retval));
	}
	(void) pwdb_delete(&pw);
	p = NULL;
	return retval;
    }
    salt = (const char *) pwe->value;

    /*
     * XXX: Cristian, the above is not the case for RADIUS(?) Some
     * lines should be added for RADIUS to verify the password in
     * clear text...
     */

    if ( ( !salt ) && ( !p ) ) {

	/* the stored password is NULL */

	(void) pwdb_entry_delete(&pwe);
	(void) pwdb_delete(&pw);

	if ( off(UNIX__NONULL, ctrl ) ) {    /* this means we've succeeded */
	    return PAM_SUCCESS;
	} else {
	    return PAM_AUTH_ERR;
	}
    }

    pp = _pam_md(p, salt);
    p = NULL;                                /* no longer needed here */

    data_name = (char *) malloc(sizeof(FAIL_PREFIX)+strlen(name));
    if ( data_name == NULL ) {
	_log_err(LOG_CRIT, "no memory for data-name");
    }
    strcpy(data_name, FAIL_PREFIX);
    strcpy(data_name + sizeof(FAIL_PREFIX)-1, name);

    /* the moment of truth -- do we agree with the password? */

    if ( strcmp( pp, salt ) == 0 ) {

	retval = PAM_SUCCESS;
	if (data_name) {                     /* reset failures */
	    pam_set_data(pamh, data_name, NULL, _cleanup_failures);
	}

    } else {

	retval = PAM_AUTH_ERR;
	if (data_name != NULL) {
	    struct _pam_failed_auth *new=NULL;
	    const struct _pam_failed_auth *old=NULL;

	    /* get a failure recorder */

	    new = (struct _pam_failed_auth *)
		malloc(sizeof(struct _pam_failed_auth));

	    if (new != NULL) {

		/* any previous failures for this user ? */
		pam_get_data(pamh, data_name, (const void **)&old );

		if (old != NULL) {
		    new->count = old->count +1;
		    if (new->count >= UNIX_MAX_RETRIES) {
			retval = PAM_MAXTRIES;
		    }
		} else {
		    new->count = 1;
		}
		new->user = xstrdup(name);
		new->id = getuid();
		new->name = xstrdup(getlogin() ? getlogin():"" );

		pam_set_data(pamh, data_name, new, _cleanup_failures);

	    } else {
		_log_err(LOG_CRIT, "no memory for failure recorder");
	    }
	}

    }

    (void) pwdb_entry_delete(&pwe);
    (void) pwdb_delete(&pw);
    salt = NULL;
    _pam_delete(data_name);
    _pam_delete(pp);

    return retval;
}

/*
 * this function obtains the name of the current user and ensures
 * that the PAM_USER item is set to this value
 */

static int _unix_get_user(pam_handle_t *pamh, unsigned int ctrl
			  , const char *prompt, const char **user)
{
    int retval;

    D(("called"));

    retval = pam_get_user(pamh, user, prompt);

    /*
     * Various libraries at various times have had bugs related to
     * '+' or '-' as the first character of a user name. Don't take
     * any chances here. Require that the username starts with an
     * alphanumeric character.
     */

    if (!isalnum(**user)) {
	_log_err(LOG_ERR, "bad username [%s]", *user);
	return PAM_USER_UNKNOWN;
    }

    if (retval == PAM_SUCCESS && on(UNIX_DEBUG,ctrl)) {
	_log_err(LOG_DEBUG, "username [%s] obtained", *user);
    }

    return retval;
}

/*
 * _unix_blankpasswd() is a quick check for a blank password
 *
 * returns TRUE if user does not have a password
 * - to avoid prompting for one in such cases (CG)
 */

static int _unix_blankpasswd(unsigned int ctrl, const char *name)
{
    const struct pwdb *pw=NULL;
    const struct pwdb_entry *pwe=NULL;
    int retval;

    D(("called"));

    /*
     * This function does not have to be too smart if something goes
     * wrong, return FALSE and let this case to be treated somewhere
     * else (CG)
     */

    if ( on(UNIX__NONULL, ctrl) )
	return 0;                     /* will fail but don't let on yet */

    /* find the user's database entry */

    retval = pwdb_locate("user", PWDB_DEFAULT, name, PWDB_ID_UNKNOWN, &pw);
    if (retval != PWDB_SUCCESS || pw == NULL ) {

	retval = 0;

    } else {

	/* Does this user have a password? */

	retval = pwdb_get_entry(pw, "passwd", &pwe);
	if ( retval != PWDB_SUCCESS || pwe == NULL )
	    retval = 0;
	else if ( pwe->value == NULL || ((char *)pwe->value)[0] == '\0' )
	    retval = 1;
	else
	    retval = 0;

    }

    /* tidy up */

    if ( pw ) {
	(void) pwdb_delete(&pw);
	if ( pwe )
	    (void) pwdb_entry_delete(&pwe);
    }

    return retval;
}

/*
 * obtain a password from the user
 */

static int _unix_read_password( pam_handle_t *pamh
				, unsigned int ctrl
				, const char *comment
				, const char *prompt1
				, const char *prompt2
				, const char *data_name
				, const char **pass )
{
    int authtok_flag;
    int retval;
    const char *item;
    char *token;

    D(("called"));

    /*
     * make sure nothing inappropriate gets returned
     */

    *pass = token = NULL;

    /*
     * which authentication token are we getting?
     */

    authtok_flag = on(UNIX__OLD_PASSWD,ctrl) ? PAM_OLDAUTHTOK:PAM_AUTHTOK ;

    /*
     * should we obtain the password from a PAM item ?
     */

    if ( on(UNIX_TRY_FIRST_PASS,ctrl) || on(UNIX_USE_FIRST_PASS,ctrl) ) {
	retval = pam_get_item(pamh, authtok_flag, (const void **) &item);
	if (retval != PAM_SUCCESS ) {
	    /* very strange. */
	    _log_err(LOG_ALERT
		     , "pam_get_item returned error to unix-read-password"
		);
	    return retval;
	} else if (item != NULL) {    /* we have a password! */
	    *pass = item;
	    item = NULL;
	    return PAM_SUCCESS;
	} else if (on(UNIX_USE_FIRST_PASS,ctrl)) {
	    return PAM_AUTHTOK_RECOVER_ERR;       /* didn't work */
	} else if (on(UNIX_USE_AUTHTOK, ctrl)
		   && off(UNIX__OLD_PASSWD, ctrl)) {
	    return PAM_AUTHTOK_RECOVER_ERR;
	}
    }

    /*
     * getting here implies we will have to get the password from the
     * user directly.
     */

    {
	struct pam_message msg[3],*pmsg[3];
	struct pam_response *resp;
	int i,expect;

	/* prepare to converse */

	if ( comment != NULL && off(UNIX__QUIET, ctrl) ) {
	    pmsg[0] = &msg[0];
	    msg[0].msg_style = PAM_TEXT_INFO;
	    msg[0].msg = comment;
	    i = 1;
	} else {
	    i = 0;
	}

	pmsg[i] = &msg[i];
	msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
	msg[i++].msg = prompt1;

	if ( prompt2 != NULL ) {
	    pmsg[i] = &msg[i];
	    msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
	    msg[i++].msg = prompt2;
	    expect = 2;
	} else
	    expect = 1;

	resp = NULL;

	/* so call the conversation expecting 'expect' responses */

	retval = converse(pamh, ctrl, i, pmsg, &resp);

	if (resp != NULL) {

	    /* interpret the response */

	    if (retval == PAM_SUCCESS) {     /* a good conversation */

		token = xstrdup(resp[0].resp);
		if (token != NULL) {
		    if (expect == 2) {

			/* verify that password entered correctly */
			if (!resp[1].resp
			    || strcmp(token,resp[1].resp)) {
			    token = _pam_delete(token); /* mistyped */
			    retval = PAM_AUTHTOK_RECOVER_ERR;
			    make_remark(pamh, ctrl
					, PAM_ERROR_MSG, MISTYPED_PASS);
			}
		    }

		} else {
		    _log_err(LOG_NOTICE
			     , "could not recover authentication token");
		}

	    }

	    /*
	     * tidy up the conversation (resp_retcode) is ignored
	     * -- what is it for anyway? AGM
	     */

	    for (i=0; i<expect; ++i) {    /* tidy up */
		resp[i].resp = _pam_delete(resp[i].resp);
	    }
	    free(resp);

	} else {
	    retval = (retval == PAM_SUCCESS)
		? PAM_AUTHTOK_RECOVER_ERR:retval ;
	}
    }

    if (retval != PAM_SUCCESS) {
	if ( on(UNIX_DEBUG,ctrl) )
	    _log_err(LOG_DEBUG,"unable to obtain a password");
	return retval;
    }

    /* 'token' is the entered password */

    if ( off(UNIX_NOT_SET_PASS, ctrl) ) {

	/* we store this password as an item */

	retval = pam_set_item(pamh, authtok_flag, token);
	token = _pam_delete(token);   /* clean it up */
	if ( retval != PAM_SUCCESS
	     || (retval = pam_get_item(pamh, authtok_flag
				       , (const void **)&item))
	     != PAM_SUCCESS ) {

	    _log_err(LOG_CRIT, "error manipulating password");
	    return retval;

	}

    } else {
	/*
	 * then store it as data specific to this module. pam_end()
	 * will arrange to clean it up.
	 */

	retval = pam_set_data(pamh, data_name, (void *) token, _cleanup);
	if (retval != PAM_SUCCESS) {
	    _log_err(LOG_CRIT, "error manipulating password data [%s]"
		     , pam_strerror(retval) );
	    token = _pam_delete(token);
	    return retval;
	}
	item = token;
	token = NULL;           /* break link to password */

    }

    *pass = item;
    item = NULL;                 /* break link to password */

    return PAM_SUCCESS;
}

static int _pam_unix_approve_pass(pam_handle_t *pamh
				  , unsigned int ctrl
				  , const char *pass_old
				  , const char *pass_new)
{
     D(("&new=%p, &old=%p",pass_old,pass_new));
     D(("new=[%s]",pass_new));
     D(("old=[%s]",pass_old));

     if (pass_new == NULL || (pass_old && !strcmp(pass_old,pass_new))) {
	  if ( on(UNIX_DEBUG, ctrl) ) {
	       _log_err(LOG_DEBUG, "bad authentication token");
	  }
	  make_remark(pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
		       "No password supplied":"Password unchanged" );
	  return PAM_AUTHTOK_ERR;
     }

     /*
      * if one wanted to hardwire authentication token strength
      * checking this would be the place - AGM
      */

     return PAM_SUCCESS;
}

/* ****************************************************************** *
 * Copyright (c) Andrew G. Morgan, <morgan@@parc.power.net> 1996.
 * Copyright (c) Alex O. Yuriev, 1996.
 * Copyright (c) Cristian Gafton 1996.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, and the entire permission notice in its entirety,
 *    including the disclaimer of warranties.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote
 *    products derived from this software without specific prior
 *    written permission.
 * 
 * ALTERNATIVELY, this product may be distributed under the terms of
 * the GNU Public License, in which case the provisions of the GPL are
 * required INSTEAD OF the above restrictions.  (This clause is
 * necessary due to a potential bad interaction between the GPL and
 * the restrictions contained in a BSD-style copyright.)
 * 
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

@


1.6
log
@added helper binary to password checking
@
text
@d2 1
a2 1
 * $Id: support.-c,v 1.5 1996/12/01 03:05:54 morgan Exp morgan $
d5 3
d88 1
d91 1
a91 1
#define UNIX_CTRLS_              18     /* number of ctrl arguments defined */
d116 1
d434 5
a438 3
#ifdef PAM_FAIL_DELAY_ON
    D(("setting delay"));
    (void) pam_fail_delay(pamh, 1000000);   /* 1 sec delay for wrong passwd */
a880 19
/* read a file up to the character 'ch' */

static int _readto(FILE *fin, char *buffer, char ch, int len)
{
     int at,c='?';

     for (at=0, --len; at >= len || (c=fgetc(fin)) != EOF
	       ; buffer[at++] = c) {
	  if (c == ch)
	       break;
     }

     buffer[at] = '\0';
     if (c == EOF)
	  return -1;

     return (c == ch) ? 1:0 ;         /* 0 indicates more to read */
}

d883 1
a883 1
 * Copyright (c) Alex 0. Yuriev, 1996.
@


1.5
log
@debugging with _pam_macros.h
@
text
@d2 1
a2 1
 * $Id: support.-c,v 1.4 1996/11/10 21:06:07 morgan Exp morgan $
d5 3
d365 51
d475 7
a481 1
	_log_err(LOG_ALERT, "get passwd; %s", pwdb_strerror(retval));
d484 1
a484 1
	return PAM_AUTHINFO_UNAVAIL;
@


1.4
log
@pwdb conversion
@
text
@d2 1
a2 1
 * $Id: support.-c,v 1.3 1996/09/05 06:49:36 morgan Exp morgan $
d5 2
a6 8
 * Revision 1.3  1996/09/05 06:49:36  morgan
 * lots. Read the diff
 *
 * Revision 1.2  1996/09/01 01:18:40  morgan
 * Cristian Gafton's patches
 *
 * Revision 1.1  1996/08/29 13:27:51  morgan
 * Initial revision
d65 2
a66 1
#define UNIX_DEBUG                3
d81 1
d84 1
a84 1
#define UNIX_CTRLS_              17     /* number of ctrl arguments defined */
d94 1
a94 1
/* UNIX_DEBUG */          { "debug",          _ALL_ON_,             010 },
d105 1
a105 1
/* UNIX_NULLOK */         { "nullok",         _ALL_ON_^(01000),       0 },
d108 1
d120 1
a120 1
    openlog("PAM-Unix", LOG_CONS|LOG_PID, LOG_AUTH);
a125 31
#ifdef DEBUG

/* This is for debugging purposes ONLY. DO NOT use on live systems !!!
 * You have been warned :-) - CG
 *
 * to get automated debugging to the log file, it must be created manually.
 * LOGFILE must exist, mode 666
 */
#define LOGFILE "/tmp/pam-debug.log"

static void _debug_pam(const char *format, ...)
{
    va_list args;
    FILE *logfile;
    
    va_start(args, format);

    if (!(logfile = fopen(LOGFILE,"a"))) {
	vfprintf(stderr, format, args);
    } else {
	vfprintf(logfile, format, args);
	fflush(logfile);
	fclose(logfile);
    }

    va_end(args);
}

#undef LOGFILE
#endif /* DEBUG */

d135 1
a135 1
    D(("begin to converse\n"));
d143 1
a143 1
	D(("returned from application's conversation function\n"));
d155 1
a155 1
    D(("ready to return from module conversation\n"));
d185 2
d191 1
a191 1
    if ( getuid() == 0 ) {
d212 1
a212 1
	D(("pam_unix arg: %s\n",*argv));
d237 6
a247 9
/* a convenient helper function to overwrite passwords */

static void _pam_overwrite(register char *xx)
{
     if (xx != NULL)
          while (*xx)
               *xx++ = '\0';
}

d252 3
a254 3
     _pam_overwrite(xx);
     free(xx);
     return NULL;
d259 1
a259 1
     x = _pam_delete( (char *) x );
d320 2
d373 1
a373 1
    D(("entering.\n"));
d402 7
a408 1
	_log_err(LOG_ALERT, "check pass; %s", pwdb_strerror(retval));
d494 1
a494 1
		new->name = xstrdup(getlogin());
d522 12
a533 1
     int retval;
d535 4
a538 1
     retval = pam_get_user(pamh, user, prompt);
d540 3
a542 15
     /*
      * Various libraries at various times have had bugs related to
      * '+' or '-' as the first character of a user name. Don't take
      * any chances here. Require that the username starts with an
      * alphanumeric character.
      */

     if (!isalnum(**user)) {
	  _log_err(LOG_ERR, "bad username [%s]", *user);
	  return PAM_USER_UNKNOWN;
     }

     if (retval == PAM_SUCCESS && on(UNIX_DEBUG,ctrl)) {
	  _log_err(LOG_DEBUG, "username [%s] obtained", *user);
     }
d544 1
a544 1
     return retval;
d560 2
d583 1
a583 1
	if ( retval != PWDB_SUCCESS || pwe == NULL || pwe->value != NULL )
d585 2
d588 1
a588 1
	    retval = 1;
d620 2
d793 3
a795 3
     D(("&new=%p, &old=%p\n",pass_old,pass_new));
     D(("new=[%s]\n",pass_new));
     D(("old=[%s]\n",pass_old));
@


1.3
log
@lots. Read the diff
@
text
@d2 1
a2 1
 * $Id: support.-c,v 1.2 1996/09/01 01:18:40 morgan Exp morgan $
d5 3
d22 1
a22 8
#define MISTYPED_PASS "Sorry, passwords do not match."

/*
 * the entry in the /etc/passwd file's password field if the user has
 * a shadow password
 */

#define SHADOW_TOKEN          "x"
d83 3
d88 1
a88 1
#define UNIX_CTRLS_              14     /* number of ctrl arguments defined */
d91 1
a91 1
static UNIX_Ctrls unix_args[UNIX_CTRLS_] = {
d108 4
a111 1
/* UNIX_MD5_PASS */       { "md5",            _ALL_ON_,          020000 }
d114 1
a114 6
/*
 * TODO: depending on the level of shadow support, perhaps make the default
 * UNIX_SHADOW iff HAVE_SHADOW_H defined ..?
 */

#define UNIX_DEFAULTS  0
d131 1
a131 1
/* This is for debugging purposes ONLY DO NOT use on life systems !!!
d141 2
a142 2
     va_list args;
     FILE *logfile;
d144 1
a144 1
     va_start(args, format);
d146 7
a152 7
     if (!(logfile = fopen(LOGFILE,"a"))) {
	  vfprintf(stderr, format, args);
     } else {
	  vfprintf(logfile, format, args);
	  fflush(logfile);
	  fclose(logfile);
     }
d154 1
a154 1
     va_end(args);
d166 2
a167 2
     int retval;
     struct pam_conv *conv;
d169 1
a169 1
     D(("begin to converse\n"));
d171 2
a172 2
     retval = pam_get_item( pamh, PAM_CONV, (const void **) &conv ) ; 
     if ( retval == PAM_SUCCESS ) {
d174 2
a175 2
	  retval = conv->conv(nargs, ( const struct pam_message ** ) message
			      , response, conv->appdata_ptr);
d177 1
a177 1
	  D(("returned from application's conversation function\n"));
d179 4
a182 4
	  if (retval != PAM_SUCCESS && on(UNIX_DEBUG,ctrl) ) {
	       _log_err(LOG_DEBUG, "conversation failure [%s]"
			, pam_strerror(retval));
	  }
d184 4
a187 4
     } else {
	  _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
	       , pam_strerror(retval));
     }
d189 1
a189 1
     D(("ready to return from module conversation\n"));
d191 1
a191 1
     return retval;                  /* propagate error status */
d197 3
a199 3
     if ( off(UNIX__QUIET, ctrl) ) {
	  struct pam_message *pmsg[1], msg[1];
	  struct pam_response *resp;
d201 8
a208 8
	  pmsg[0] = &msg[0];
	  msg[0].msg = text;
	  msg[0].msg_style = type;
	  resp = NULL;

	  return converse(pamh, ctrl, 1, pmsg, &resp);
     }
     return PAM_SUCCESS;
d217 1
a217 1
     unsigned int ctrl;
d219 1
a219 1
     ctrl = UNIX_DEFAULTS;         /* the default selection of options */
d221 1
a221 1
     /* set some flags manually */
d223 45
a267 15
     if ( getuid() == 0 ) {
	  set(UNIX__IAMROOT, ctrl);
     }
     if ( flags & PAM_UPDATE_AUTHTOK ) {
	  set(UNIX__UPDATE, ctrl);
     }
     if ( flags & PAM_PRELIM_CHECK ) {
	  set(UNIX__PRELIM, ctrl);
     }
     if ( flags & PAM_DISALLOW_NULL_AUTHTOK ) {
	  set(UNIX__NONULL, ctrl);
     }
     if ( flags & PAM_SILENT ) {
	  set(UNIX__QUIET, ctrl);
     }
d269 1
a269 13
     /* now parse the arguments to this module */

     while (argc-- > 0) {
	  int j;

	  D(("pam_unix arg: %s\n",*argv));

	  for (j=0; j<UNIX_CTRLS_; ++j) {
	       if (unix_args[j].token
		   && ! strcmp(*argv, unix_args[j].token) ) {
		    break;
	       }
	  }
d271 1
a271 13
	  if ( j >= UNIX_CTRLS_ ) {
	       _log_err(LOG_ERR, "unrecognized option [%s]",*argv);
	  } else {
	       ctrl &= unix_args[j].mask;    /* for turning things off */
	       ctrl |= unix_args[j].flag;    /* for turning things on  */
	  }

	  ++argv;                           /* step to next argument */
     }

     /* return the set of flags */

     return ctrl;
d283 1
a283 1
/* use this to free password strings */
d297 4
a300 1
/* safe duplication of character strings */
d304 4
a307 1
     char *new=NULL;
d309 11
a319 7
     if (x != NULL) {
	  if ((new = malloc(strlen(x)+1)) == NULL) {
	       _log_err(LOG_CRIT, "out of memory in xstrdup");
	  } else {
	       strcpy(new,x);
	  }
     }
d321 1
a321 1
     return new;           /* return the duplicate or NULL on error */
d328 1
a328 53
/*
 * This function is a front-end for the message digest algorithms used
 *  to compute the user's encrypted passwords. No reversible encryption
 * is used here and I intend to keep it that way.
 *
 * While there are many sources of encryption outside the United
 * States, it *may* be illegal to re-export reversible encryption
 * computer code. Until such time as it is legal to export encryption
 * software freely from the US, please do not send me any. (AGM)
 */

/* this should have been defined in a header file.. Why wasn't it? AGM */
extern char *crypt(const char *key, const char *salt);

#include "md5.h"

struct cfns {
     const char *salt;
     int len;
     char * (* mdfn)(const char *key, const char *salt);
};

/* array of non-standard digest algorithms available */

#define N_MDS 1
const static struct cfns cfn_list[N_MDS] = {
     { "$1$", 3, crypt_md5 },
};

static char *_pam_md(const char *key, const char *salt)
{
     char *x,*e=NULL;
     int i;

     D(("entering\n"));

     /* check for non-standard salts */

     for (i=0; i<N_MDS; ++i) {
	  if ( !strncmp(cfn_list[i].salt, salt, cfn_list[i].len) ) {
	       e = cfn_list[i].mdfn(key, salt);
	       break;
	  }
     }

     if ( i >= N_MDS ) {
	  e = crypt(key, salt);   /* default to standard algorithm in libc */
     }

     x = xstrdup(e);                         /* put e in malloc()ed memory */
     _pam_overwrite(e);                                        /* clean up */
     return x;                           /* this must be deleted elsewhere */
}
d339 4
a342 4
     char *user;                  /* user that's failed to be authenticated */
     char *name;                  /* attempt from user with name */
     int id;                      /* uid of name'd user */
     int count;                   /* number of failures so far */
d351 28
a378 23
     int quiet;
     const char *service=NULL;
     struct _pam_failed_auth *failure;

     quiet = err & PAM_DATA_SILENT;     /* should we log something? */
     err  &= PAM_DATA_REPLACE;          /* are we just replacing data? */
     failure = (struct _pam_failed_auth *) fl;

     if ( failure != NULL ) {

	  if ( !quiet && !err ) {   /* under advisement from Sun/May go away */

	       /* log the number of authentication failures */
	       if ( failure->count != 0 ) {
		    (void) pam_get_item(pamh, PAM_SERVICE
					, (const void **)&service);
		    _log_err(LOG_NOTICE
			     , "%d authentication failure%s; %s(uid=%d) -> "
			       "%s for %s service"
			     , failure->count, failure->count==1 ? "":"s"
			     , failure->name
			     , failure->id
			     , failure->user
d380 9
a388 13
			 );
		    if ( failure->count > UNIX_MAX_RETRIES ) {
			 _log_err(LOG_ALERT
				  , "service(%s) ignoring max retries; %d > %d"
				  , service == NULL ? "**unknown**":service
				  , failure->count
				  , UNIX_MAX_RETRIES );
		    }
	       }
	  }
	  free(failure->user);	                      /* tidy up */
	  free(failure);
     }
d395 10
a404 12
static int _unix_verify_password(pam_handle_t *pamh
				 , const char *name, const char *p
				 , unsigned int ctrl)
{
     struct passwd *pw;
#ifdef HAVE_SHADOW_H
     struct spwd *sp;
#endif
     const char *salt;
     char *pp;
     char *data_name;
     int retval;
d406 1
a406 1
     D(("entering.\n"))
d409 2
a410 1
     (void) pam_fail_delay(pamh, 1000000);   /* 1 sec delay for wrong passwd */
d413 102
a514 86
     pw = getpwnam (name);
     if (pw) {                           /* does user exist? */

	  if ( strcmp(pw->pw_passwd, SHADOW_TOKEN) == 0 ) {
#ifdef HAVE_SHADOW_H
	       /*
		* Linux-PAM is pluggable, so the sys-admin may have
		* some users authenticated against shadow entries and
		* some without. An "x" in the user's passwd field of
		* the /etc/passwd file indicates the use of
		* /etc/shadow is manditory for that user. An 'x' will be
		* placed there by the chauthtok functions if 'shadow' is
		* supplied as an argument of the module.
		*/

	       sp = getspnam( name );
	       if (sp) {
		    salt = sp->sp_pwdp;
	       } else {
		    _log_err(LOG_ALERT, "user %s incorrectly shadowed", name);
		    return PAM_USER_UNKNOWN;
	       }
#else
	       _log_err(LOG_ALERT
			, "user %s requires shadow support - replace module!"
			, name);
	       return PAM_USER_UNKNOWN;
#endif
	  } else {
	       salt = pw->pw_passwd;
	  }
     } else {
	  _log_err(LOG_NOTICE, "user %s unknown", name);
	  return PAM_USER_UNKNOWN;
     }

     /*
      * The 'always-encrypt' method does not make sense in PAM because
      *	the framework requires return of a different error code for
      *	non-existant users -- alex
      */

     if ( ( !pw->pw_passwd ) && ( !p ) )
	  if ( off(UNIX__NONULL, ctrl ) )
	       return PAM_SUCCESS;
	  else
	       return PAM_AUTH_ERR;

     pp = _pam_md(p, salt);
     p = NULL;                     /* no longer needed here */

     data_name = (char *) malloc(sizeof(FAIL_PREFIX)+strlen(name));
     if ( data_name == NULL ) {
	  _log_err(LOG_CRIT, "no memory for data-name");
     }
     strcpy(data_name, FAIL_PREFIX);
     strcpy(data_name + sizeof(FAIL_PREFIX)-1, name);

     if ( strcmp( pp, salt ) == 0 ) {
	  if (data_name) {                     /* reset failures */
	       pam_set_data(pamh, data_name, NULL, _cleanup_failures);
	  }
	  retval = PAM_SUCCESS;
     } else {
	  retval = PAM_AUTH_ERR;
	  if (data_name != NULL) {
	       struct _pam_failed_auth *new=NULL;
	       const struct _pam_failed_auth *old=NULL;

	       /* get a failure recorder */

	       new = (struct _pam_failed_auth *)
		    malloc(sizeof(struct _pam_failed_auth));

	       if (new != NULL) {

		    /* any previous failures for this user ? */
		    pam_get_data(pamh, data_name, (void *)&old );

		    if (old != NULL) {
			 new->count = old->count +1;
			 if (new->count >= UNIX_MAX_RETRIES) {
			      retval = PAM_MAXTRIES;
			 }
		    } else {
			 new->count = 1;
d516 21
a536 11
		    new->user = xstrdup(name);
		    new->id = getuid();
		    new->name = xstrdup(getlogin());

		    pam_set_data(pamh, data_name, new, _cleanup_failures);

	       } else {
		    _log_err(LOG_CRIT, "no memory for failure recorder");
	       }
	  }
     }
d538 1
a538 2
     free(data_name);
     return retval;
a549 1
     char *luser;
d551 1
a551 2
     retval = pam_get_user(pamh, &luser, prompt);
     *user = luser;
d553 10
a562 5
     if (retval == PAM_SUCCESS) {
	  if (on(UNIX_DEBUG,ctrl)) {
	       _log_err(LOG_DEBUG, "username [%s] obtained", *user);
	  }
	  retval = pam_set_item(pamh, PAM_USER, *user);
d565 2
a566 3
     if (retval != PAM_SUCCESS) {
	  _log_err(LOG_ERR, "unable to set username [%s]"
		   , pam_strerror(retval));
d568 1
a568 1
     
d581 19
a599 4
     struct passwd *pwent;
#ifdef HAVE_SHADOW_H
     struct spwd *spent;
#endif
d601 1
a601 5
     /*
      * This function does not have to be too smart if something goes
      * wrong, return FALSE and let this case to be treated somewhere
      * else (CG)
      */
d603 1
a603 2
     if ( on(UNIX__NONULL, ctrl) )
	  return 0;                    /* will fail but don't let on yet */
d605 5
a609 6
     setpwent();
     pwent=getpwnam(name);
     if (!pwent) {
	  endpwent();
          return 0;
     }
d611 1
a611 6
#ifdef HAVE_SHADOW_H
     /*
      * Note, this is a little tricky and it could fail if no
      * /etc/shadow is present e.g. we have compiled SHADOW but we are
      * not on a shadowed system ... CG
      */
d613 1
a613 7
     setspent();   /* initialize shadow managment */
     spent = getspnam(name);
     if (spent) {
	  int i = strlen(spent->sp_pwdp);
          endspent();                     /* done with shadow */
          return (!i);
     }
d615 5
a619 2
     endspent();                     /* done with shadow */
#endif
d621 1
a621 2
     endpwent();
     return (strlen(pwent->pw_passwd) == 0);
d636 98
a733 103
     int authtok_flag;
     int retval;
     const char *item;
     char *token;

     /*
      * make sure nothing inappropriate gets returned
      */

     *pass = token = NULL;

     /*
      * which authentication token are we getting?
      */

     authtok_flag = on(UNIX__OLD_PASSWD,ctrl) ? PAM_OLDAUTHTOK:PAM_AUTHTOK ;

     /*
      * should we obtain the password from a PAM item ?
      */

     if ( on(UNIX_TRY_FIRST_PASS,ctrl) || on(UNIX_USE_FIRST_PASS,ctrl) ) {
	  retval = pam_get_item(pamh, authtok_flag, (const void **) &item);
	  if (retval != PAM_SUCCESS ) {
	       /* very strange. */
	       _log_err(LOG_ALERT
			, "pam_get_item returned error to unix-read-password"
		    );
	       return retval;
	  } else if (item != NULL) {    /* we have a password! */
	       *pass = item;
	       item = NULL;
	       return PAM_SUCCESS;
	  } else if (on(UNIX_USE_FIRST_PASS,ctrl)) {
	       return PAM_AUTHTOK_RECOVER_ERR;       /* didn't work */
	  } else if (on(UNIX_USE_AUTHTOK, ctrl)
		     && off(UNIX__OLD_PASSWD, ctrl)) {
	       return PAM_AUTHTOK_RECOVER_ERR;
	  }
     }

     /*
      * getting here implies we will have to get the password from the
      * user directly.
      */

     {
	  struct pam_message msg[3],*pmsg[3];
	  struct pam_response *resp;
	  int i,expect;

	  /* prepare to converse */

	  if ( comment != NULL && off(UNIX__QUIET, ctrl) ) {
	       pmsg[0] = &msg[0];
	       msg[0].msg_style = PAM_TEXT_INFO;
	       msg[0].msg = comment;
	       i = 1;
	  } else {
	       i = 0;
	  }

	  pmsg[i] = &msg[i];
	  msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
	  msg[i++].msg = prompt1;

	  if ( prompt2 != NULL ) {
	       pmsg[i] = &msg[i];
	       msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
	       msg[i++].msg = prompt2;
	       expect = 2;
	  } else
	       expect = 1;

	  resp = NULL;

	  /* so call the conversation expecting 'expect' responses */

	  retval = converse(pamh, ctrl, i, pmsg, &resp);

	  if (resp != NULL) {

	       /* interpret the response */

	       if (retval == PAM_SUCCESS) {     /* a good conversation */

		    token = xstrdup(resp[0].resp);
		    if (token != NULL) {
			 if (expect == 2) {

			      /* verify that password entered correctly */
			      if (!resp[1].resp
				  || strcmp(token,resp[1].resp)) {
				   token = _pam_delete(token); /* mistyped */
				   retval = PAM_AUTHTOK_RECOVER_ERR;
				   make_remark(pamh, ctrl
					       , PAM_ERROR_MSG, MISTYPED_PASS);
			      }
			 }

		    } else {
			 _log_err(LOG_NOTICE
				  , "could not recover authentication token");
d736 4
a739 25
	       }

	       /*
		* tidy up the conversation (resp_retcode) is ignored
		* -- what is it for anyway? AGM
		*/

	       for (i=0; i<expect; ++i) {    /* tidy up */
		    resp[i].resp = _pam_delete(resp[i].resp);
	       }
	       free(resp);

	  } else {
	       retval = (retval == PAM_SUCCESS)
		    ? PAM_AUTHTOK_RECOVER_ERR:retval ;
	  }
     }

     if (retval != PAM_SUCCESS) {
	  if ( on(UNIX_DEBUG,ctrl) )
	       _log_err(LOG_DEBUG,"unable to obtain a password");
	  return retval;
     }

     /* 'token' is the entered password */
d741 1
a741 1
     if ( off(UNIX_NOT_SET_PASS, ctrl) ) {
d743 55
a797 1
	  /* we store this password as an item */
d799 1
a799 6
	  retval = pam_set_item(pamh, authtok_flag, token);
	  token = _pam_delete(token);   /* clean it up */
	  if ( retval != PAM_SUCCESS
	      || (retval = pam_get_item(pamh, authtok_flag
					, (const void **)&item))
	       != PAM_SUCCESS ) {
d801 2
a802 2
	       _log_err(LOG_CRIT, "error manipulating password");
	       return retval;
d804 1
a804 24
	  }

     } else {
	  /*
	   * then store it as data specific to this module. pam_end()
	   * will arrange to clean it up.
	   */

	  retval = pam_set_data(pamh, data_name, (void *) token, _cleanup);
	  if (retval != PAM_SUCCESS) {
	       _log_err(LOG_CRIT, "error manipulating password data [%s]"
		    , pam_strerror(retval) );
	       token = _pam_delete(token);
	       return retval;
	  }
	  item = token;
	  token = NULL;           /* break link to password */

     }

     *pass = item;
     item = NULL;                 /* break link to password */

     return PAM_SUCCESS;
d820 2
d827 1
a827 1
      * checking this would be the place
d853 1
a853 1
 * Copyright (c) Andrew G. Morgan, 1996.
@


1.2
log
@Cristian Gafton's patches
@
text
@d2 1
a2 1
 * $Id: support.-c,v 1.1 1996/08/29 13:27:51 morgan Exp $
d5 3
d21 7
d132 31
d289 1
a289 1
static int _cleanup(pam_handle_t *pamh, void *x, int error_status)
a291 1
     return PAM_SUCCESS;
d316 114
a437 3
     const char *salt;
     char *pp;

d441 4
d446 1
a446 1
     D(("entering\n."))
a451 15
     /* SECURITY !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
      * THE FOLOWING CODE GIVES FREE ACCESS TO EVERYONE
      * ON THE SYSTEM. IT IS BORKEN. COMMENTED OUT FOR NOW.
      * Just press ENTER when prompted for the passwd.
      * cristian gafton. Ouch ! 
      */
/*
     if ( on(UNIX__IAMROOT, ctrl) ) {
	  if ( on(UNIX_DEBUG, ctrl) ) {
	       _log_err(LOG_DEBUG, "no verification of password [uid = 0]");
	  }
	  return PAM_SUCCESS;
     }
*/

d453 1
a453 1
     if (pw) {
d455 1
a456 8
	  /*
	   * Support for shadow passwords on Linux and SVR4-based
	   * systems.  Shadow passwords are optional on Linux - if
	   * there is no shadow password, use the non-shadow one.
	   */

	  sp = getspnam( name );
	  if (sp) {
d458 7
a464 1
		* TODO: check if password has expired etc.
d466 13
a478 2
	       salt = sp->sp_pwdp;
	  } else
d480 5
a484 3

	  salt = pw->pw_passwd;
     } else
d486 1
d493 1
a493 1
		
d495 1
a495 1
	  if ( on (UNIX__NONULL, ctrl ) )
d500 1
a500 1
     pp = crypt(p, salt);
d503 39
a541 2
     if ( strcmp( pp, salt ) == 0 ) 
	  return PAM_SUCCESS;
d543 10
a552 1
     return PAM_AUTH_ERR;
d585 52
d863 1
a863 1
 * Copyright (c) Andrew G. Morgan, 1996-.  All rights reserved.
d865 1
@


1.1
log
@Initial revision
@
text
@d2 1
a2 1
 * $Id$
d4 2
a5 2
 * $Log: support.c,v $
 * Revision 1.1  1996/04/17 01:11:08  alex
d76 1
d79 1
a79 1
#define UNIX_CTRLS_              13     /* number of ctrl arguments defined */
d99 1
d291 2
d297 7
d310 1
@
