/* 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 /* 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