/**************************************************
OpengateM - MAC address authentication system 
 module for Authentication by PAM

Copyright (C) 2002 Opengate Project Team
Written by Yoshiaki Watanabe

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 (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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

Email: watanaby@is.saga-u.ac.jp
**************************************************/
/*
  Thanks to programs and documentations refered.
   The Linux-PAM Application Developer's Guide by A. G. Morgan.
   The example appilication in above doc by Shane Watts
   Manual files about PAM.
*/

#include <security/pam_appl.h>
#include "opengatemmng.h"

int pamCallback(int num_msg, const struct pam_message **msg,
		    struct pam_response **resp, void *appdata_ptr);

static struct pam_conv pamConv; /* PAM conversation info */

typedef struct {
  char *userid;
  char *password;
} userInfo_t;

/******************************/
/* authentication by PAM      */
/* need to edit /etc/pam.conf */
/******************************/
int authPam(char *userid, char *passwd)
{
  char serviceName[ADDRMAXLN];
  char *serviceNameInConf;
  pam_handle_t *pamh=NULL;
  int retval;
  userInfo_t userInfo;
  
  /* get pam service name used in pam config file */
  serviceNameInConf=GetConfValue("AuthServer/ServiceName");

  /* if not defined in conf, use the default name 'opengate' */
  if(isNull(serviceNameInConf)){
    strlcpy(serviceName, PAMSERVICENAME, ADDRMAXLN);
  }else{
    strlcpy(serviceName, serviceNameInConf, ADDRMAXLN);
  }
  
  if(!userid || !passwd) return DENY;
  
  /* set userInfo which is passed to call-back function */
  userInfo.userid=userid;
  userInfo.password=passwd;
  
  /* setting of call back function (its name and data) */
  pamConv.conv=pamCallback;
  pamConv.appdata_ptr=&userInfo;
  
  /* root privilege is needed to control PAM */
  if(seteuid(0)!=0){
    err_msg("ERR at %s#%d: cannot add root privilege ",
	    __FILE__,__LINE__);
  } 

  /* PAM start */
  retval = pam_start(serviceName, userid, &pamConv, &pamh);
  
  /* at success, check auth */
  if (retval == PAM_SUCCESS){
    retval = pam_authenticate(pamh, 0);    /* is user really user? */
  }
  
  /* at success, check account */
  if (retval == PAM_SUCCESS){
    retval = pam_acct_mgmt(pamh, 0);       /* permitted access? */
  }
  
  /* error message if not success */
  if (retval != PAM_SUCCESS){
    err_msg("ERR at %s#%d: %s\n",__FILE__,__LINE__,pam_strerror(pamh, retval));
  }
  
  /* PAM end */
  if (pam_end(pamh,retval) != PAM_SUCCESS) {
    pamh = NULL;
    err_msg("ERR at %s#%d: failed to release authenticator",__FILE__,__LINE__);
  }
  
  /* remove root privilege */
  seteuid(getuid()); 

  /* clear password */
  userInfo.password = NULL;

  return ( retval == PAM_SUCCESS ? ACCEPT:DENY );       /* indicate success */
}

/**************************************************************/
/* Send user information to PAM. Called back from PAM module. */
/**************************************************************/
int pamCallback(int num_msg, const struct pam_message **msg,
		    struct pam_response **resp, void *appdata_ptr)
{
  userInfo_t *userInfo = (userInfo_t *)appdata_ptr;
  struct pam_response *response=NULL;
  int i;
  
  /* parameter check */
  if(!resp || !msg || !userInfo) return PAM_CONV_ERR;

  /* allocate memory to store response */
  response = (struct pam_response *)
    malloc(num_msg * sizeof(struct pam_response));
  if(!response) return PAM_CONV_ERR;

  /* set user informations */
  for(i=0; i<num_msg; i++){
    response[i].resp_retcode=0;
    response[i].resp=NULL;

    switch(msg[i]->msg_style){
    case PAM_PROMPT_ECHO_ON:   /* must be requesting userid */
      response[i].resp=(char *)strdup(userInfo->userid);
      break;
    case PAM_PROMPT_ECHO_OFF:   /* must be requesting password */
      response[i].resp=(char *)strdup(userInfo->password);
      break;                 
    default:                 
      break;                 
    }
  }

  *resp = response;
  return PAM_SUCCESS;
}

/************ For DEBUG ********************/
int AuthPam(char *userid, char *passwd)
{
  int ret;

  if(debug>1) err_msg("DEBUG:=>authPam(%s,passwd)",userid);
  ret=authPam(userid, passwd);
  if(debug>1) err_msg("DEBUG:(%d)<=authPam( )",ret);

  return ret;
}
