/* plugin for 3proxy with PAM auth only for *NIX (linux,*bsd)
Kirill Lopuchov <lopuchov@mail.ru>

   Compile with: gcc -shared -o pamauth.so pamauth.c -lpam  -DNOODBC
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <security/pam_appl.h>



#include "../../proxy.h"
#include "../../structures.h"

int         already_loaded = 0;

static struct auth myalwaysauth;
static int     usercaselow = 0;
unsigned char *service=NULL;
struct pluginlink * mypluginlink;


static int password_conversation ( int num_msg, const struct pam_message **msg, 
					struct pam_response **resp, 
					void *appdata_ptr);

int auth_pam(const char *name, const char *key);
					
static char *password = NULL;


password_conversation ( int num_msg,  const struct pam_message **msg, 
					struct pam_response **resp, 
					 void *appdata_ptr)
  {
   if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF)
    {
     return PAM_CONV_ERR;
    }
   if (!appdata_ptr) appdata_ptr = password;
    if (!appdata_ptr)
     {
      return PAM_CONV_ERR;
     }
     *resp = calloc (num_msg, sizeof (struct pam_response));
   if (!*resp)
    {
     return PAM_CONV_ERR;
    }
    (*resp)[0].resp = strdup ((char *) appdata_ptr);
    (*resp)[0].resp_retcode = 0;
    return ((*resp)[0].resp ? PAM_SUCCESS : PAM_CONV_ERR);
  }

void lower (char *string)
{
 int length, i;
 
 length = strlen(string);
 for (i=0; i<length; i++)
 {
    string[i] = tolower(string[i]);
 }
}

/*--------------------------------------------------------------------------*/


int start(struct pluginlink * pluginlink, 
					 int argc, char** argv);


/* --------------------------------------------------------------------------*/
static int pamfunc(struct clientparam *param)
 {
  pam_handle_t *pamh = NULL;
  int retval;
  int rc=0;

  struct pam_conv conv = {
        &password_conversation,
        NULL };

  /* test proxy user auth ------------------------*/
  if(!param->username || !param->password) return 4;
  /*if(strlen(param->password)==0) return 4;*/

  #ifdef USERCASE
   if (usercaselow > 0) 
   { lower(param->username);  }
  #endif

  /*start process auth */  
  conv.appdata_ptr = (char *) param->password;

  if (!pamh)
    {
      retval = pam_start (service, "3proxy@" , &conv, &pamh);
    }
   if (retval == PAM_SUCCESS)
       retval = pam_set_item (pamh, PAM_USER, param->username); 
/*fprintf(stderr,"pam_set_item1 rc=%d\n",retval);*/
   if (retval == PAM_SUCCESS)
       retval = pam_set_item (pamh, PAM_CONV, &conv); 
/*fprintf(stderr,"pam_set_item2 rc=%d\n",retval); */       
   if (retval == PAM_SUCCESS)
         retval = pam_authenticate (pamh, 0);  
/*fprintf(stderr,"pam_authenticate rc=%d\n",retval);*/
   
   if (retval == PAM_SUCCESS) {  /*auth OK*/  rc=0;   }
   else  { /*auth ERR*/  rc=5;     }

   if (pamh)
      retval = pam_end (pamh, retval);
   if (retval != PAM_SUCCESS)
      {  pamh = NULL;   }

  return rc;

}

/*------------------------------- MAIN --------------------------------------
 start plugin init  */
int start(struct pluginlink * pluginlink, int argc, char** argv)
{
  
  
   if (service==NULL){ 
	service=pluginlink->mystrdup(argv[1]); 
   }
  else  {
     pluginlink->myfree(service);
     service=pluginlink->mystrdup(argv[1]);
   }

   if (already_loaded != 0) { return (0); }

    already_loaded = 1;
    
    mypluginlink=pluginlink;
 
    myalwaysauth.authenticate = pamfunc;
    myalwaysauth.authorize = pluginlink->checkACL;
    myalwaysauth.desc = "pam";
    myalwaysauth.next = pluginlink->authfuncs->next;
    pluginlink->authfuncs->next = &myalwaysauth;
  
    return 0;
}


