/*
 * bbftpd/bbftpd_login.c
 * Copyright (C) 1999, 2000, 2001, 2002 IN2P3, CNRS
 * bbftp@in2p3.fr
 * http://doc.in2p3.fr/bbftp
 *
 * 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
 * of the License, or 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */ 

/****************************************************************************

  
 RETURN:
         0  login OK
        -1 login failed
 
 bbftpd_login.c  v 1.6.0  2000/03/24    - Creation of the routine. This part of
                                          code was contained in readcontrol.c
                 v 1.8.0  2000/04/14    - Introduce RSA Cryptage
                 v 1.8.3  2000/04/18    - Change the reply message in order
                                          to stop retry in case of unkown user
                                          or incorrect password
                 v 1.8.7  2000/05/24    - Modify headers
                 v 1.8.11 2000/05/24    - Add Pam for Linux (and only pam...)
                 v 1.9.0  2000/08/18    - Use configure to help portage
                                        - Change the reply message (for future
                                          use)
                 v 1.9.0  2000/09/19    - Correct bug for pam
                 v 1.9.4  2000/10/16    - Supress %m
                 v 2.0.0  2000/12/18    - Use incontrolsock instead of msgsock
                 v 2.0.1  2001/04/23    - Correct indentation
                 v 2.0.2  2001/05/04    - Correct return code treatment
                                        - Change use of tmpchar
                 v 2.1.0  2001/06/11    - Change file name
                                     
 *****************************************************************************/

#include <errno.h>
#include <netinet/in.h>
#include <pwd.h>
#include <syslog.h>
#include <sys/types.h>
#include <unistd.h>
#include <utime.h>

#include <bbftpd.h>
#include <config.h>
#include <common.h>
#include <daemon.h>
#include <daemon_proto.h>
#include <structures.h>
#include <version.h>

#ifdef SHADOW_PASSWORD
#include <shadow.h>
#endif
#ifdef USE_PAM
#include <security/pam_appl.h>
#endif
#ifdef HAVE_SECURITY_PASS
#include <userpw.h>
#endif

extern int incontrolsock ;
extern	int	recvcontrolto ;
extern char currentusername[MAXLEN] ;

#ifdef USE_PAM
extern char daemonchar[50] ;

static char *PAM_password;
/* PAM conversation function
 * We assume that echo on means login name, and echo off means password.
 */
static int PAM_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
{
    int replies = 0;
    struct pam_response *reply = NULL;

#define COPY_STRING(s) (s) ? strdup(s) : NULL

    reply = (struct pam_response *) malloc(sizeof(struct pam_response) * num_msg);
    if (!reply) return PAM_CONV_ERR;

    for (replies = 0; replies < num_msg; replies++) {
        switch (msg[replies]->msg_style) {
        case PAM_PROMPT_ECHO_ON:
            return PAM_CONV_ERR;
            break;
        case PAM_PROMPT_ECHO_OFF:
            reply[replies].resp_retcode = PAM_SUCCESS;
            reply[replies].resp = COPY_STRING(PAM_password);
            /* PAM frees resp */
            break;
        case PAM_TEXT_INFO:
            /* ignore it... */
            reply[replies].resp_retcode = PAM_SUCCESS;
            reply[replies].resp = NULL;
            break;
        case PAM_ERROR_MSG:
            /* ignore it... */
            reply[replies].resp_retcode = PAM_SUCCESS;
            reply[replies].resp = NULL;
            break;
        default:
           /* Must be an error of some sort... */
            return PAM_CONV_ERR;
        }
    }
    *resp = reply;
    return PAM_SUCCESS;
}

static struct pam_conv PAM_conversation =
{
    &PAM_conv,
    NULL
};
#endif

int loginsequence() {
    
    int        retcode ;
    char    receive_buffer[MAXMESSLEN] ;
    struct mess_sec *msg_sec ;
    struct mess_rsa *msg_rsa ;
    
    char    username[MAXLEN] ;
    char    password[MAXLEN] ;
    struct    passwd    *uspass ;
    char    logmessage[1024] ;
#ifdef SHADOW_PASSWORD
    struct    spwd    *sunpass ;
#endif
#ifdef HAVE_SECURITY_PASS
    struct    userpw    *secpass ;
#endif
    char    *calcpass ;
#ifdef AFS
    char    *inst = "";
#endif
#ifdef USE_PAM
    pam_handle_t *pamh=NULL;
#endif
    sprintf(logmessage,"bbftpd version %s",VERSION) ;
    /*
    ** Read the crypt type
    */
    if ( (retcode = readmessage(incontrolsock,receive_buffer,CRYPTMESSLEN,recvcontrolto)) < 0 ) {
        /*
        ** Error ...
        */
        return(retcode) ;
    }
    msg_sec = (struct mess_sec *)receive_buffer ;
    switch (msg_sec->crtype) {
        case CRYPT_RSA_PKCS1_OAEP_PADDING :
            /*
            ** RSA
            */
            if ( (retcode = readmessage(incontrolsock,receive_buffer,RSAMESSLEN,recvcontrolto)) < 0) {
                return(retcode) ;
            }
            if ( decodersapass(receive_buffer,username,password) < 0 ) {
                syslog(BBFTPD_ERR,"Decode user/password error") ;
                strcat(logmessage," : Decode user/password error") ;
                reply(MSG_BAD,logmessage) ;
                return -1 ;
            }
            break ;
        case NO_CRYPT :
            if ( (retcode = readmessage(incontrolsock,receive_buffer,RSAMESSLEN,recvcontrolto)) < 0) {
                return(retcode) ;
            }
            msg_rsa = (struct mess_rsa *)receive_buffer ;
            strncpy(username, msg_rsa->cryptuser, strlen(msg_rsa->cryptuser));
            username[strlen(msg_rsa->cryptuser)]='\0';
            strncpy(password, msg_rsa->cryptpass, strlen(msg_rsa->cryptpass));
            password[strlen(msg_rsa->cryptpass)]='\0';
            break ;
        default :
            syslog(BBFTPD_ERR,"Unkwown encryption %d",msg_sec->crtype) ;
            strcat(logmessage," : Unknwon encryption") ;
            reply(MSG_BAD,logmessage) ;
            return -1 ;
    }
    /*
    ** Here we check the username and pass and set the default dir
    */
    if ( (uspass = getpwnam(username)) == NULL ) {
        syslog(BBFTPD_ERR,"Unknown user %s",username) ;
        strcat(logmessage," : Unknown user (") ;
        strcat(logmessage, username);
        strcat(logmessage, ")");
        reply(MSG_BAD_NO_RETRY,logmessage) ;
        return -1 ;
    }
/*
** If the AFS flag is set it take precedence on all other mechanism
*/
#ifdef AFS
    setpag() ;
    retcode = ka_UserAuthenticate(username,inst,0,password,0,&calcpass) ;
    if ( retcode != 0 ) {
        syslog(BBFTPD_ERR,"ka_UserAuthenticate message : %s ",calcpass) ;
        /*
        ** Check local user 
        */
        if ( (strcmp(uspass->pw_passwd,"x") == 0) || 
             (strcmp(uspass->pw_passwd,"X") == 0) ) {
#ifdef SHADOW_PASSWORD
            if ( (sunpass = getspnam(username)) == NULL ) {
                syslog(BBFTPD_ERR,"Unknown user %s",username) ;
                /*
                ** We send ka_UserAuthenticate error msg
                */
                strcat(logmessage, " : ") ;
                strcat(logmessage, calcpass) ;
                reply(MSG_BAD_NO_RETRY,logmessage) ;
                return -1 ;
            }
            calcpass = (char *) crypt(password,sunpass->sp_pwdp) ;
            if ( strcmp(calcpass,sunpass->sp_pwdp) != 0 ) {
                syslog(BBFTPD_ERR,"Incorrect password user %s",username) ;
                strcat(logmessage," : Incorrect password") ;
                reply(MSG_BAD_NO_RETRY,logmessage) ;
                return -1 ;
            }
#elif defined(HAVE_SECURITY_PASS)
            if ( (secpass = getuserpw(username)) == NULL ) {
                syslog(BBFTPD_ERR,"Unknown user %s",username) ;
                /*
                ** We send ka_UserAuthenticate error msg
                */
                strcat(logmessage, " : ") ;
                strcat(logmessage, calcpass) ;
                reply(MSG_BAD_NO_RETRY,logmessage) ;
                return -1 ;
            }
            calcpass = (char *) crypt(password,secpass->upw_passwd) ;
            if ( strcmp(calcpass,secpass->upw_passwd) != 0 ) {
                syslog(BBFTPD_ERR,"Incorrect password user %s",username) ;
                strcat(logmessage," : Incorrect password") ;
                reply(MSG_BAD_NO_RETRY,logmessage) ;
                return -1 ;
            }
#else

            syslog(BBFTPD_ERR,"No Password user %s",username) ;
            strcat(logmessage," : No password user") ;
            reply(MSG_BAD_NO_RETRY,logmessage) ;
            return -1 ;
#endif
        } else {
            calcpass = (char *) crypt(password,uspass->pw_passwd) ;
            if ( strcmp(calcpass,uspass->pw_passwd) != 0 ) {
                syslog(BBFTPD_ERR,"Incorrect password user %s",username) ;
                strcat(logmessage," : Incorrect password") ;
                reply(MSG_BAD_NO_RETRY,logmessage) ;
                return -1 ;
            }
        }
    }
#elif defined(USE_PAM)
    /*
    ** For PAM
    */
    strcat(logmessage," : PAM Error") ;
    PAM_password = password ;
    retcode = pam_start("bbftp", username, &PAM_conversation, &pamh);
#define PAM_BAIL if (retcode != PAM_SUCCESS) { pam_end(pamh, 0); syslog(BBFTPD_ERR,"PAM error (%d) user %s",retcode,username) ;reply(MSG_BAD_NO_RETRY,logmessage) ;return -1; }
    PAM_BAIL;
    retcode = pam_authenticate(pamh, PAM_SILENT);
    PAM_BAIL;
    retcode = pam_acct_mgmt(pamh, 0);
    PAM_BAIL;
#define PAM_ESTABLISH_CRED 1
#ifdef PAM_ESTABLISH_CRED
    retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
#else
    retcode = pam_setcred(pamh, PAM_CRED_ESTABLISH);
#endif
    PAM_BAIL;
    pam_end(pamh, PAM_SUCCESS);
    /*
    ** Reopen the lag as the pam functions close it
    */
    openlog(daemonchar, LOG_PID | LOG_NDELAY, LOG_DAEMON);
#else
    /*
    ** For the others
    */
    if ( (strcmp(uspass->pw_passwd,"x") == 0) || 
         (strcmp(uspass->pw_passwd,"X") == 0)) {
#ifdef SHADOW_PASSWORD
            if ( (sunpass = getspnam(username)) == NULL ) {
                syslog(BBFTPD_ERR,"Unknown user %s",username) ;
                strcat(logmessage," : Unknown user") ;
                reply(MSG_BAD_NO_RETRY,logmessage) ;
                return -1 ;
            }
            calcpass = (char *) crypt(password,sunpass->sp_pwdp) ;
            if ( strcmp(calcpass,sunpass->sp_pwdp) != 0 ) {
                syslog(BBFTPD_ERR,"Incorrect password user %s",username) ;
                strcat(logmessage," : Incorrect password") ;
                reply(MSG_BAD_NO_RETRY,logmessage) ;
                return -1 ;
            }
#elif defined(HAVE_SECURITY_PASS)
            if ( (secpass = getuserpw(username)) == NULL ) {
                syslog(BBFTPD_ERR,"Unknown user %s",username) ;
                strcat(logmessage," : Unknown user") ;
                reply(MSG_BAD_NO_RETRY,logmessage) ;
                return -1 ;
            }
            calcpass = (char *) crypt(password,secpass->upw_passwd) ;
            if ( strcmp(calcpass,secpass->upw_passwd) != 0 ) {
                syslog(BBFTPD_ERR,"Incorrect password user %s",username) ;
                strcat(logmessage," : Incorrect password") ;
                reply(MSG_BAD_NO_RETRY,logmessage) ;
                return -1 ;
            }
#else
            syslog(BBFTPD_ERR,"No Password user %s",username) ;
            strcat(logmessage," : No password") ;
            reply(MSG_BAD_NO_RETRY,logmessage) ;
            return -1 ;
#endif
    } else {
        calcpass = (char *) crypt(password,uspass->pw_passwd) ;
        if ( strcmp(calcpass,uspass->pw_passwd) != 0 ) {
            syslog(BBFTPD_ERR,"Incorrect password user %s",username) ;
            strcat(logmessage," : Incorrect password") ;
            reply(MSG_BAD_NO_RETRY,logmessage) ;
            return -1 ;
        }
    }
#endif
    /*
    ** Set the uid and gid of the process
    */
    if ( setgid(uspass->pw_gid) < 0 ) {
        syslog(BBFTPD_ERR,"Error setgid user %s : %s",username,strerror(errno)) ;
        strcat(logmessage," : Unable to set gid: ") ;
	strcat(logmessage,strerror(errno));
        reply(MSG_BAD,logmessage) ;
        return -1 ;
    }
    if ( setuid(uspass->pw_uid) < 0 ) {
        syslog(BBFTPD_ERR,"Error setuid user %s : %s",username,strerror(errno)) ;
        strcat(logmessage," : Unable to set uid: ") ;
	strcat(logmessage,strerror(errno));
        reply(MSG_BAD,logmessage) ;
        return -1 ;
    }
    if ( uspass->pw_dir == NULL ) {
        syslog(BBFTPD_ERR,"No home directory user %s : %s",username,strerror(errno)) ;
        strcat(logmessage," : No home directory: ") ;
	strcat(logmessage,strerror(errno));
        reply(MSG_BAD,logmessage) ;
        return -1 ;
    }
    if ( chdir(uspass->pw_dir) < 0) {
        syslog(BBFTPD_ERR,"Error chdir user %s : %s",username,strerror(errno)) ;
        strcat(logmessage," : Unable to change directory: ") ;
	strcat(logmessage,strerror(errno));
        reply(MSG_BAD,logmessage) ;
        return -1 ;
    }
                
    syslog(BBFTPD_DEBUG,"User %s connected",username) ;
    strcpy(currentusername,username) ;
    return 0 ;
}


syntax highlighted by Code2HTML, v. 0.9.1