/* Copyright (C) 1999 Beau Kuiper
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., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "ftpd.h"
#include "auth.h"
#ifdef HAVE_PAM_START
/* Are we debugging pam at the moment? */
#define PAMDEBUG
#ifdef PAMDEBUG
#define PAMLOG(handle, name, user, result) log_giveentry(MYLOG_INFO, NULL, safe_snprintf(name" on %s returned %d. (%s)", user, result, pam_strerror(handle, result)))
#else
#define PAMLOG(handle, name, user, result) NULL
#endif
#include <security/pam_appl.h>
/* This file contains code to autheticate using PAM password. It was written
using proftpd as a reference */
typedef struct
{
char *password;
char *username;
char *homedir;
char *passwduser;
int uid;
int gid;
pam_handle_t *pamh;
int retval;
} PASSWDSTRUCT;
PASSWDSTRUCT *currenthandle = NULL;
static int pamauth_conv(int num_msg, struct pam_message **msg,
struct pam_response **resp,
void *appdata_ptr)
{
struct pam_response *response = NULL;
int count;
/* I cannot use mallocwrapper here because it will break counters
since PAM takes the memory */
response = malloc(sizeof(struct pam_response) * num_msg);
if (response == NULL)
return(PAM_CONV_ERR);
for (count = 0; count < num_msg; count++)
{
response[count].resp_retcode = PAM_SUCCESS;
switch(msg[count]->msg_style)
{
case PAM_PROMPT_ECHO_ON:
if (currenthandle == NULL)
ERRORMSGFATAL("PAM is being mean! It wants a username I don't know");
response[count].resp = strdup(currenthandle->username);
if (!response[count].resp)
return(PAM_CONV_ERR);
break;
case PAM_PROMPT_ECHO_OFF:
if (currenthandle == NULL)
ERRORMSGFATAL("PAM is being mean! It wants a password I don't know");
response[count].resp = currenthandle->password;
break;
case PAM_TEXT_INFO:
case PAM_ERROR_MSG:
response[count].resp = NULL;
break;
default: /* yuck, an error! */
free(response);
return(PAM_CONV_ERR);
}
}
*resp = response;
return(PAM_SUCCESS);
}
static struct pam_conv mypam = {
&pamauth_conv,
NULL
};
void pamauth_freehandle(void *h)
{
PASSWDSTRUCT *handle = (PASSWDSTRUCT *)h;
pam_end(handle->pamh, handle->retval);
freeifnotnull(handle->homedir);
freewrapper(handle->passwduser);
freewrapper(handle->username);
freewrapper(handle);
}
void *pamauth_gethandle(FTPSTATE *peer, TOKENSET *tset, char *username, int *err)
{
PASSWDSTRUCT *newhandle;
char *pamservice = NULL;
char *pamuser = NULL;
struct passwd *passent;
pamservice = mktokconfstr(tset, auth_getcursectionid(peer), "pam_service", "ftp");
pamuser = mktokconfstr(tset, auth_getcursectionid(peer), "pam_user", "%U");
newhandle = mallocwrapper(sizeof(PASSWDSTRUCT));
newhandle->username = strdupwrapper(username);
newhandle->passwduser = pamuser;
newhandle->homedir = NULL;
/* if we can't get the username from the password file, assume it
doesn't exist */
*err = AUTH_USERNKNOW;
passent = getpwnam(pamuser);
if (passent == NULL)
{
if (strcmp(username, pamuser) == 0)
log_giveentry(MYLOG_INFO, NULL, safe_snprintf("Username '%s' does not exist for pam authentication", pamuser));
goto error;
}
newhandle->homedir = strdupwrapper(passent->pw_dir);
newhandle->uid = passent->pw_uid;
newhandle->gid = passent->pw_gid;
newhandle->pamh = NULL;
newhandle->retval = pam_start(pamservice, username, &mypam, &(newhandle->pamh));
/* lets log all stuff that pam does! */
PAMLOG(newhandle->pamh, "pam_start", username, newhandle->retval);
/* if above didn't work, it is a nasty error and should cancel
authentication */
*err = AUTH_ERROR;
if (newhandle->retval != PAM_SUCCESS)
goto error;
*err = AUTH_OK;
freewrapper(pamservice);
return(newhandle);
error:
pamauth_freehandle(newhandle);
freewrapper(pamservice);
return(NULL);
}
int pamauth_checkpasswd(void *h, char *password, char **errmsg)
{
PASSWDSTRUCT *handle = (PASSWDSTRUCT *)h;
currenthandle = handle;
handle->password = strdup(password);
if (!handle->password)
ERRORMSGFATAL("out of memory in PAM module!");
/* This will authenticate the password */
/* I think it uses all the auth PAM configuration lines */
handle->retval = pam_authenticate(handle->pamh, PAM_SILENT);
PAMLOG(handle->pamh, "pam_authenticate", handle->username, handle->retval);
/* This part now checks the users account */
if (handle->retval == PAM_SUCCESS)
handle->retval = pam_acct_mgmt(handle->pamh, 0);
else
return(FALSE);
PAMLOG(handle->pamh, "pam_acct_mgmt", handle->username, handle->retval);
if (handle->retval == PAM_SUCCESS)
handle->retval = pam_setcred(handle->pamh, PAM_ESTABLISH_CRED);
else
{
*errmsg = safe_snprintf("Account Problem");
return(FALSE);
}
PAMLOG(handle->pamh, "pam_setcred", handle->username, handle->retval);
if (handle->retval == PAM_SUCCESS)
return(TRUE);
*errmsg = safe_snprintf("Credantial Problem");
return(FALSE);
}
char *pamauth_gethomedir(void *h)
{
return(((PASSWDSTRUCT *)h)->homedir);
}
char *pamauth_getrootdir(void *h)
{
return("/");
}
uid_t pamauth_getuseruid(void *h)
{
return(((PASSWDSTRUCT *)h)->uid);
}
gid_t pamauth_getusergid(void *h)
{
return(((PASSWDSTRUCT *)h)->gid);
}
gid_t *pamauth_getusersupgid(void *h)
{
return(getusergrouplist(((PASSWDSTRUCT *)h)->passwduser));
}
PERMSTRUCT pamauth_commands =
{
pamauth_checkpasswd,
pamauth_gethomedir,
pamauth_getrootdir,
pamauth_getuseruid,
pamauth_getusergid,
pamauth_getusersupgid,
pamauth_gethandle,
pamauth_freehandle,
NULL,
};
#endif
syntax highlighted by Code2HTML, v. 0.9.1