/** BEGIN COPYRIGHT BLOCK
 * Copyright (C) 2001 Sun Microsystems, Inc.  Used by permission.
 * Copyright (C) 2005 Red Hat, Inc.
 * All rights reserved.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation version
 * 2.1 of the License.
 *                                                                                  
 * This library 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
 * Lesser General Public License for more details.
 *                                                                                  
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 * END COPYRIGHT BLOCK **/
#include <stdio.h>
#ifdef XP_UNIX
#include <libgen.h>
#endif
#include "ldap.h"
#include "libadmsslutil/psetcssl.h"
#include "libadmsslutil/admsslutil.h"
#include "cert.h"
#include "secmod.h"
#include "ssl.h"
#include "nss.h"
#include "pkcs11.h"
#include "secpwd_pvt.h"
#include "prprf.h"
#include "sslproto.h"
#include "libadminutil/admutil-int.h"

#include "errno.h"
#include "secport.h"
#include "libadmsslutil/certmgt.h"
#include "version.h"

#define ADMSSL_BUF_LEN 1024

#define IDEDIT 1000
#define IDD_PASSWORD 108
#define IDTOKEN 1004
                   
#ifndef FILE_PATHSEP
#define FILE_PATHSEP '/'
#endif

#define SECURITY_ATTR "configuration.nsServerSecurity"

void servssl_error(char *fmt, ...);

char custom_file[BIG_LINE] = "";
char retried_token[BIG_LINE] = "";

PR_IMPLEMENT(int)
admldapBuildInfoSSL(AdmldapInfo info, int *errorcode)
{
  LDAP  *ld;
  int   ldapError;
  char *passwd = NULL;
  char *host = NULL;

  *errorcode = ADMUTIL_OP_OK;

  host = admldapGetHost(info);
  if (admldapGetSecurity(info))
    ld = admutil_ldap_init(info, NULL, host, admldapGetPort(info), 1, 0, NULL);
  else
    ld = admutil_ldap_init(info, NULL, host, admldapGetPort(info), 0, 0, NULL);
  PL_strfree(host);

  if (!ld) {
    *errorcode = ADMUTIL_LDAP_ERR;
    return 0;
  }

  passwd = admldapGetSIEPWD(info);
  if (passwd) {
      char *dn = admldapGetSIEDN(info);
      ldapError = admutil_ldap_bind(ld, dn, passwd, LDAP_SASL_SIMPLE, NULL, NULL, NULL, NULL);
      if (ldapError != LDAP_SUCCESS) {
#ifdef LDAP_DEBUG
        fprintf(stderr, "admutil_ldap_bind: [%s] error [%d:%s]\n", dn, ldapError, ldap_err2string(ldapError));
#endif
      }
      PL_strfree(dn);
      memset(passwd, '\0', strlen(passwd));
      PL_strfree(passwd);
  } else {
      /* no password means just punt rather than do anon bind */
      /* this mimics the same logic in admldapBuildInfoCbk() */
      ldap_unbind_ext(ld, NULL, NULL);
      *errorcode = ADMUTIL_LDAP_ERR;
      return 1; /* have to return true here to mimic admldapBuildInfoCbk() */
  }
  /* authenticate to LDAP server*/
  if (ldapError != LDAP_SUCCESS) {
    switch (ldapError) {
    case LDAP_INAPPROPRIATE_AUTH:
    case LDAP_INVALID_CREDENTIALS:
    case LDAP_INSUFFICIENT_ACCESS:
    case LDAP_UNWILLING_TO_PERFORM:
      /* authenticate failed: Should not continue */
      break;
    case LDAP_NO_SUCH_OBJECT:
    case LDAP_ALIAS_PROBLEM:
    case LDAP_INVALID_DN_SYNTAX:
      /* Not a good user DN */
      ldap_unbind_ext(ld, NULL, NULL);
      *errorcode = ADMUTIL_LDAP_ERR;
      return 0;
      break;
    default:
      ldap_unbind_ext(ld, NULL, NULL);
      *errorcode = ADMUTIL_LDAP_ERR;
      return 0;
    }
  }

  admldapSetLDAPHndl(info, ld);
  return 1;
}

static int initNSS(const char *securitydir, AdmldapInfo info)
{

  char *custom_file_copy = NULL;
  PRUint32 flags = 0;
  char *db_name;
  SSLVersionRange range;
  SSLProtocolVariant variant;

  /* PKSC11 module must be configured before NSS is initialized */
  db_name = PL_strdup("internal (software)              ");
  if (!db_name) {
      return -1;
  }

  PK11_ConfigurePKCS11(NULL,NULL,NULL,db_name,NULL, NULL,NULL,NULL,
                       /*minPwdLen=*/8, /*pwdRequired=*/1);
  PL_strfree(db_name);
  db_name = NULL;
 
  /* init NSS */ 
  if (NSS_Initialize(securitydir, NULL, NULL, SECMOD_DB, flags)) {
      return -1;
  }

  /* custom file should contain a line like this:
	 pinFile:/path/to/pinfile
	 The pin file should contain the pin for the token
	 We just use adm.conf as the custom file.
  */
  if (info) {
    custom_file_copy = admldapGetConfigFileName(info);
    PR_snprintf(custom_file, sizeof(custom_file), custom_file_copy);
    PL_strfree(custom_file_copy);
    /* set password function */
    PK11_SetPasswordFunc(getPassword_cb);
  }

  /* enable all default ciphers */
  if (SSL_OptionSetDefault(SSL_ENABLE_SSL2, PR_FALSE)) {
      return -1;
  }

  range.min = admldapGetSSLMin(info);
  range.max = admldapGetSSLMax(info);
  variant = ssl_variant_stream;
  SSL_VersionRangeSetDefault(variant, &range);

  /* set export policy */
  if(SSLPLCY_Install() != PR_SUCCESS) {
    return -1;
  }

  return 0;
}


PR_IMPLEMENT(int)
ADMSSL_InitSimple(char* configdir, char *securitydir, int force)
{
  AdmldapInfo   admLdapInfo=NULL;
  int  error;

  admLdapInfo = admldapBuildInfo(configdir, &error);
  if (!admLdapInfo && !force) return -1;

  if (force || (admLdapInfo && admldapGetSecurity(admLdapInfo))) {
      error = ADMSSL_Init(admLdapInfo, securitydir, force);
  } else {
      error = 0;
  }

  destroyAdmldap(admLdapInfo);
  return error;
}

/* set minimum SSL stuff for LDAP/SSL to work */
PR_IMPLEMENT(int)
ADMSSL_Init(AdmldapInfo info, char *securitydir, int force)
{
  int   secure = 0;
  char *dirURL, *bindDN, *bindPwd, *dirInfoRef; 
  int errCode;
  int needfree = 0;

  if (force) {
    secure = 1;
  } else if (info) {
    secure = admldapGetSecurity(info);
  }

  if (!secure && info) {
    admldapGetLocalUserDirectory(info,
                                 &dirURL, 
                                 &bindDN, 
                                 &bindPwd, 
                                 &dirInfoRef, 
                                 &errCode);
    if (errCode || !dirURL) return 0;
    if (!strstr(dirURL, "ldaps://"))
        return 0;
  }

  if (!securitydir && info) {
    securitydir = admldapGetSecurityDir(info);
    needfree = 1;
  }

  if((!securitydir)) return -1;

  errCode = initNSS(securitydir, info);

  if (needfree) {
    PL_strfree(securitydir);
  }

  return errCode;
}

void servssl_error(char *fmt, ...)
{
    va_list args;
    char errstr[ADMSSL_BUF_LEN];

    va_start(args, fmt);
    PR_vsnprintf(errstr, sizeof(errstr), fmt, args);
    va_end(args);
#ifdef XP_UNIX
    fprintf(stderr, "%s\n", errstr);
#else  /* XP_WIN32 */
    MessageBox(NULL, errstr,
               "SSL_INITIALIZATION_ERROR", MB_APPLMODAL|MB_ICONEXCLAMATION|MB_OK);
#endif
    exit(1);
}

void servssl_warn(char *fmt, ...)
{
    va_list args;
    char warnstr[ADMSSL_BUF_LEN];

    va_start(args, fmt);
    PR_vsnprintf(warnstr, sizeof(warnstr), fmt, args);
    va_end(args);
#ifdef XP_UNIX
    fprintf(stderr, "%s\n", warnstr);
#else  /* XP_WIN32 */
    MessageBox(NULL, warnstr,
               "SSL_INITIALIZATION_WARNING", MB_APPLMODAL|MB_ICONEXCLAMATION|MB_OK);
#endif
}


/*
 * SSLPLCY_Install
 */
PRStatus
SSLPLCY_Install(void)
{
  SECStatus s;
  /* There is no more export builds */
  s = NSS_SetDomesticPolicy();
  return (s==SECSuccess)?PR_SUCCESS:PR_FAILURE;
}


/*
 * Modify "security" in adm.conf and DS
 */
void set_security(PsetHndl pset, 
		  char *configdir, /* where config files can be found */
                  char *security /* security on/off */
)
{
  int rv;
  
  /* set security attribute in DS */
  if((pset) && (security) && (*security != '\0')) {
    if(psetCheckAttribute(pset, SECURITY_ATTR) == PSET_OP_FAIL) {
      /* create entry */
      rv = psetAddSingleValueAttribute(pset, SECURITY_ATTR, security);
      if(rv != PSET_OP_OK)
        servssl_error("PSET attribute creation failed!");      
    }
    else {
      /* modify entry */
      rv = psetSetSingleValueAttr(pset, SECURITY_ATTR, security);
      if(rv != PSET_OP_OK)
        servssl_error("PSET attribute modification failed!");
    }
  }
}


/* NT SSL STUFF HERE -------------------------------------------------------*/

#ifdef XP_WIN32
static char password[512];
static char pinprompt[512];

//--------------------------------------------------------------------------//
// borrowed from \msvc20\mfc\src\dlgcore.cpp                                //
//--------------------------------------------------------------------------//
void static CenterDialog(HWND hwndParent, HWND hwndDialog)
{
   RECT DialogRect;
   RECT ParentRect;
        POINT Point;
   int nWidth;
   int nHeight;
 
        // Determine if the main window exists. This can be useful when
   // the application creates the dialog box before it creates the
   // main window. If it does exist, retrieve its size to center
   // the dialog box with respect to the main window.
   if(hwndParent != NULL)
        {
      GetClientRect(hwndParent, &ParentRect);
        }
   else
   {
           // if main window does not exist, center with respect to desktop
           hwndParent = GetDesktopWindow();
      GetWindowRect(hwndParent, &ParentRect);
   }
 
   // get the size of the dialog box
   GetWindowRect(hwndDialog, &DialogRect);
 
   // calculate height and width for MoveWindow()
   nWidth = DialogRect.right - DialogRect.left;
   nHeight = DialogRect.bottom - DialogRect.top;
 
   // find center point and convert to screen coordinates
   Point.x = (ParentRect.right - ParentRect.left) / 2;
   Point.y = (ParentRect.bottom - ParentRect.top) / 2;

        ClientToScreen(hwndParent, &Point);
 
   // calculate new X, Y starting point
   Point.x -= nWidth / 2;
   Point.y -= nHeight / 2;
 
   MoveWindow(hwndDialog, Point.x, Point.y, nWidth, nHeight, FALSE);
}



//--------------------------------------------------------------------------//
// length and wierdness checks are removed because they                     //
// do not belong in a password entry dialog.  This behavior OK-d            //
// by jsw@netscape.com on 01/26/96                                          //
//--------------------------------------------------------------------------//
BOOL CALLBACK PasswordDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
   switch(message)
   {
   case WM_INITDIALOG:
     CenterDialog(NULL, hDlg);
     SendDlgItemMessage(hDlg, IDEDIT, EM_SETLIMITTEXT, sizeof(password), 0);
     SetDlgItemText(hDlg, IDTOKEN, pinprompt);
     EnableWindow(GetDlgItem(hDlg, IDOK), FALSE);
     return(FALSE);

   case WM_COMMAND:
     if(LOWORD(wParam) == IDEDIT)
     {
       if(HIWORD(wParam) == EN_CHANGE)
       {
         if(GetDlgItemText(hDlg, IDEDIT, password, sizeof(password)) > 0)
           EnableWindow(GetDlgItem(hDlg, IDOK), TRUE);
         else
           EnableWindow(GetDlgItem(hDlg, IDOK), FALSE);
       }
     }
     else
     if(LOWORD(wParam) == IDOK)
     {
       GetDlgItemText(hDlg, IDEDIT, password, sizeof(password));
       EndDialog(hDlg, IDOK);
       return (TRUE);
     }
     else
     if(LOWORD(wParam) == IDCANCEL)
     {
       /* User hit "Cancel" - terminate ns-admin.exe */
       memset(password, 0, sizeof(password));
       EndDialog(hDlg, IDCANCEL);
       exit(0);
     } else if (LOWORD(wParam) == IDHELP) {
       /* link will be broken if the file name is changed */
#define ADMSSLVIEWURL "start ..\\..\\bin\\base\\viewurl.exe "
#define ADMSSLFILE "file:///"
#define ADMSSLMANPATH "/manual/en/admin/help/5_secure.htm#SSL Token Password"
       char *my_netsite_root = getenv("NETSITE_ROOT");
       exec = PR_smprintf("%s%s%s%s", ADMSLVIEWURL, ADMSSLFILE, 
                                      my_netsite_root, ADMSSLMANPATH);
       if (NULL != exec) {
         system(exec);
         PR_smprintf_free(exec);
       }
     }
   }
   return (FALSE);
}

unsigned char *SEC_GetPassword(FILE *input, FILE *output, char *prompt,
                               PRBool (*ok)(unsigned char *))
{
   int result, retries = 0;

   PL_strncpyz(pinprompt, prompt, sizeof(pinprompt));
   result = DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_PASSWORD), 
                     HWND_DESKTOP, (int (__stdcall *)(void))PasswordDialogProc);
   retries = GetLastError();
   if (result == -1) {
      result = GetLastError();
      servssl_error("DialogBox failed (code %d)\n",
                    GetLastError());
      return NULL;
   }
   return (unsigned char*) PL_strdup((char*)password);
}
#endif   /* XP_WIN32 */


SECStatus MyAuthCertificateHook(void *arg, PRFileDesc *fd, PRBool checksig, PRBool isserver)
{
  return SECSuccess;
}


PR_IMPLEMENT(PRFileDesc*)
SSLSocket_init(PRFileDesc *req_socket, const char *configdir, const char *securitydir)
{
    PRFileDesc *ssl_socket = NULL;

    if (ADMSSL_InitSimple((char *)configdir, (char *)securitydir, 0) != 0) {
       PR_Close(ssl_socket);
       return NULL;
    }

    if (SSL_OptionSetDefault(SSL_ENABLE_SSL2, PR_FALSE)) {
        return NULL;
    }

    ssl_socket = SSL_ImportFD(NULL, req_socket);

    SSL_AuthCertificateHook(ssl_socket, MyAuthCertificateHook, NULL);

    if (SSL_OptionSet(ssl_socket, SSL_SECURITY, PR_TRUE) < 0) {
        PR_Close(ssl_socket);
        return NULL;
    }

    return ssl_socket;
}

/*
  emacs settings
  Local Variables:
  c-basic-offset: 2
  End:
*/
