629 lines
14 KiB
C++
629 lines
14 KiB
C++
/***********************************************************************
|
|
*
|
|
* Copyright (C) 2005-2006 Novell, 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
|
|
* Library 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, Novell, Inc.
|
|
*
|
|
* To contact Novell about this file by physical or electronic mail,
|
|
* you may find current contact information at www.novell.com.
|
|
*
|
|
***********************************************************************/
|
|
|
|
#include "SignonManager.h"
|
|
|
|
SignonManager::SignonManager()
|
|
{
|
|
signonFile = NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
SignonManager::~SignonManager()
|
|
{
|
|
|
|
}
|
|
|
|
|
|
void SignonManager::SetupFunctions(void *funList[])
|
|
{
|
|
cryptManager.SetupFunctions(funList);
|
|
}
|
|
|
|
|
|
|
|
|
|
int SignonManager::OpenSignonFile(char *firefoxProfileDir, char *fileName, char *accessType )
|
|
{
|
|
char *signonFilePath = NULL;
|
|
|
|
signonFilePath = (char*) malloc( strlen(firefoxProfileDir) + strlen(fileName) + 3 );
|
|
|
|
if( signonFilePath == NULL )
|
|
{
|
|
PrintMessage(MESG_ERROR, "\n Insufficient memory ....");
|
|
return FPM_INSUFFICIENT_MEMORY;
|
|
}
|
|
|
|
strcpy(signonFilePath, firefoxProfileDir);
|
|
strcat(signonFilePath, "/");
|
|
strcat(signonFilePath, fileName);
|
|
|
|
PrintMessage(MESG_DEBUG, "\n Final signon filename is = %s ", signonFilePath);
|
|
|
|
// Open the signon file
|
|
signonFile = fopen(signonFilePath, accessType);
|
|
|
|
if( signonFile == NULL )
|
|
{
|
|
PrintMessage(MESG_ERROR, "\n SignonManager : Error opening signon file %s", signonFilePath);
|
|
free(signonFilePath);
|
|
return FPM_SIGNON_FILE_NOT_PRESENT;
|
|
}
|
|
|
|
// cleanup
|
|
free(signonFilePath);
|
|
|
|
return FPM_TRUE;
|
|
|
|
}
|
|
|
|
|
|
int SignonManager::CloseSignonFile()
|
|
{
|
|
|
|
if( signonFile )
|
|
fclose(signonFile);
|
|
|
|
return FPM_TRUE;
|
|
|
|
}
|
|
|
|
|
|
int SignonManager::ReadLine(char *buffer, int size)
|
|
{
|
|
Unichar c;
|
|
int strLength = 0, i=0;
|
|
|
|
buffer[0] = 0;
|
|
|
|
while(1)
|
|
{
|
|
c = ReadCharUTF8();
|
|
|
|
/* note that eof is not set until we read past the end of the file */
|
|
if ( c == FPM_FALSE ) // || feof(file) )
|
|
return FPM_FALSE;
|
|
|
|
if (c == '\n')
|
|
{
|
|
buffer[strLength++] = 0;
|
|
break;
|
|
}
|
|
|
|
|
|
if (c != '\r')
|
|
{
|
|
for(i=0; i < 4 && ( (c & 0xff) != 0 ) ; i++)
|
|
{
|
|
if( strLength >= size )
|
|
{
|
|
// Increase the capacity dynamically
|
|
PrintMessage(MESG_ERROR, "SignonManager : Buffer is insufficient to store data");
|
|
return FPM_FALSE;
|
|
}
|
|
|
|
buffer[strLength++] = (char)c;
|
|
c = c >> 8;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// PrintMessage(MESG_DEBUG,"SignonManager : ReadLine = %s ",buffer);
|
|
return FPM_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Unichar SignonManager::ReadCharUTF8()
|
|
{
|
|
Unichar c = ReadChar();
|
|
|
|
if ((c & 0x80) == 0x00)
|
|
{
|
|
return c;
|
|
}
|
|
else if ((c & 0xE0) == 0xC0)
|
|
{
|
|
return (((c & 0x1F)<<6) + (ReadChar() & 0x3F));
|
|
}
|
|
else if ((c & 0xF0) == 0xE0)
|
|
{
|
|
return (((c & 0x0F)<<12) + ((ReadChar() & 0x3F)<<6) + (ReadChar() & 0x3F));
|
|
}
|
|
|
|
|
|
return FPM_FALSE; // 0 => not a utf8 character...
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// This does buffered reading...
|
|
//
|
|
char SignonManager::ReadChar()
|
|
{
|
|
const int buflen = 1000;
|
|
static char buf[buflen+1];
|
|
static int last = 0;
|
|
static int next = 0;
|
|
|
|
if (next >= last)
|
|
{
|
|
next = 0;
|
|
last = fread(buf, 1, buflen, signonFile);
|
|
PrintMessage(MESG_DEBUG,"\n SignonManager : ReadChar = Read %d bytes ",last);
|
|
|
|
if (last <= 0) // || feof(file)
|
|
{
|
|
/* note that eof is not set until we read past the end of the file */
|
|
PrintMessage(MESG_DEBUG,"\n SignonManager : ReadChar = End of file..! ");
|
|
return FPM_FALSE;
|
|
}
|
|
}
|
|
|
|
return (buf[next++]);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int SignonManager::WriteCharUTF8(Unichar c)
|
|
{
|
|
if (c <= 0x7F)
|
|
{
|
|
if( fputc((char)c, signonFile) == EOF )
|
|
return FPM_SIGNON_FILE_WRITE_ERROR;
|
|
}
|
|
else if (c <= 0x7FF)
|
|
{
|
|
if( fputc( ((Unichar)0xC0) | ((c>>6) & 0x1F), signonFile) == EOF )
|
|
return FPM_SIGNON_FILE_WRITE_ERROR;
|
|
|
|
if( fputc( ((Unichar)0x80) | (c & 0x3F), signonFile ) == EOF )
|
|
return FPM_SIGNON_FILE_WRITE_ERROR;
|
|
|
|
}
|
|
else
|
|
{
|
|
if( fputc( ((Unichar)0xE0) | ((c>>12) & 0xF), signonFile) == EOF )
|
|
return FPM_SIGNON_FILE_WRITE_ERROR;
|
|
if( fputc( ((Unichar)0x80) | ((c>>6) & 0x3F), signonFile) == EOF)
|
|
return FPM_SIGNON_FILE_WRITE_ERROR;
|
|
if( fputc( ((Unichar)0x80) | (c & 0x3F), signonFile) == EOF)
|
|
return FPM_SIGNON_FILE_WRITE_ERROR;
|
|
}
|
|
|
|
return FPM_TRUE;
|
|
}
|
|
|
|
|
|
|
|
int SignonManager::WriteLine(char *line)
|
|
{
|
|
|
|
for(int i=0; i < strlen(line); i++)
|
|
{
|
|
if( WriteCharUTF8(line[i]) != FPM_TRUE )
|
|
return FPM_SIGNON_FILE_WRITE_ERROR;
|
|
|
|
}
|
|
|
|
if( WriteCharUTF8('\n') != FPM_TRUE )
|
|
return FPM_SIGNON_FILE_WRITE_ERROR;
|
|
|
|
return FPM_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Load the signon data from firefox signon file.....
|
|
*
|
|
*
|
|
*/
|
|
int SignonManager::LoadSignonData(char *firefoxProfileDir )
|
|
{
|
|
char header[256];
|
|
char buffer[4096];
|
|
char hostName[4096];
|
|
char name[1024];
|
|
int bufferLength = 4095;
|
|
int retValue;
|
|
char *clearData = NULL;
|
|
int count = 0;
|
|
|
|
|
|
// open the signon file
|
|
if( (retValue = OpenSignonFile(firefoxProfileDir, SIGNON_FILE_NAME, "r")) != FPM_TRUE )
|
|
{
|
|
return retValue;
|
|
}
|
|
|
|
// Clean up any previously loaded data...
|
|
dataManager.RemoveAllData();
|
|
|
|
// read the signon header information
|
|
if( ReadLine(header, 256) != FPM_TRUE )
|
|
{
|
|
PrintMessage(MESG_ERROR, "\n SignonManager : Error in reading signon format header %s", header);
|
|
CloseSignonFile();
|
|
return FPM_SIGNON_FILE_READ_ERROR;
|
|
}
|
|
|
|
// check if the format is right...
|
|
if( strcmp(header, HEADER_VERSION) != 0)
|
|
{
|
|
PrintMessage(MESG_ERROR, "\n SignonManager : Header version information is not proper");
|
|
CloseSignonFile();
|
|
return FPM_SIGNON_FILE_INVALID_DATA;
|
|
}
|
|
|
|
|
|
PrintMessage(MESG_DEBUG, "\n\n ****** Reject Host List *******");
|
|
|
|
// read the reject list
|
|
while ( ReadLine(buffer, bufferLength) == FPM_TRUE)
|
|
{
|
|
// Check for end of reject list i.e full stop
|
|
if (strlen(buffer) != 0 && buffer[0] == '.')
|
|
{
|
|
break; // end of reject list
|
|
}
|
|
|
|
if( (retValue = dataManager.AddRejectHost(buffer) ) != FPM_TRUE )
|
|
{
|
|
CloseSignonFile();
|
|
dataManager.RemoveAllData(); // clean up any partial loaded data
|
|
|
|
return retValue;
|
|
}
|
|
|
|
PrintMessage(MESG_DEBUG, "\n Reject Host : %s ", buffer);
|
|
|
|
}
|
|
|
|
|
|
PrintMessage(MESG_DEBUG, "\n\n ****** Host list with username / password ****** ");
|
|
|
|
|
|
while (ReadLine(hostName, bufferLength) == FPM_TRUE)
|
|
{
|
|
// a blank line is perfectly valid here -- corresponds to a local file
|
|
if (strlen(hostName) < 1)
|
|
{
|
|
PrintMessage(MESG_ERROR, "\n SignonManager : Host URL is not proper");
|
|
CloseSignonFile();
|
|
dataManager.RemoveAllData(); // clean up partial loaded data
|
|
|
|
return FPM_ILLEGAL_HOSTNAME;
|
|
}
|
|
|
|
if( ( retValue = dataManager.AddHost(hostName) ) != FPM_TRUE )
|
|
{
|
|
CloseSignonFile();
|
|
dataManager.RemoveAllData(); // clean up partial loaded data
|
|
|
|
return retValue;
|
|
}
|
|
|
|
|
|
PrintMessage(MESG_DEBUG, "\n\n Host : %s ", hostName);
|
|
|
|
// prepare to read the name/value pairs
|
|
while( ReadLine(buffer, bufferLength) == FPM_TRUE )
|
|
{
|
|
// line starting with . terminates the pairs for this URL entry
|
|
if (buffer[0] == '.')
|
|
{
|
|
break; // end of URL entry
|
|
}
|
|
|
|
// save the name part and determine if it is a password
|
|
unsigned char isPassword = 0;
|
|
if (buffer[0] == '*')
|
|
{
|
|
isPassword = 1;
|
|
strcpy(name,&buffer[1]);
|
|
retValue = ReadLine(buffer, bufferLength);
|
|
}
|
|
else
|
|
{
|
|
isPassword = 0;
|
|
strcpy(name, buffer);
|
|
retValue = ReadLine(buffer, bufferLength);
|
|
}
|
|
|
|
PrintMessage(MESG_DEBUG, "\n\n name = %s and value = %s ", name, buffer);
|
|
|
|
// read in and save the value part
|
|
if ( retValue != FPM_TRUE )
|
|
{
|
|
// error in input file so give up
|
|
PrintMessage(MESG_ERROR, "\n SignonManager : Error occured while reading VALUE for : %s ", name);
|
|
CloseSignonFile();
|
|
dataManager.RemoveAllData(); // clean up partial loaded data
|
|
|
|
return FPM_SIGNON_FILE_READ_ERROR;
|
|
}
|
|
|
|
// Decrypt the encrypted value....
|
|
retValue = FPM_FALSE;
|
|
if( ((retValue = cryptManager.DecryptString(buffer, &clearData)) == FPM_TRUE) && (clearData != NULL) )
|
|
{
|
|
// Add the name/value pair to the existing store....
|
|
retValue = dataManager.AddHostElement(hostName, name, clearData, isPassword);
|
|
|
|
if( retValue != FPM_TRUE )
|
|
{
|
|
CloseSignonFile();
|
|
dataManager.RemoveAllData(); // clean up partial loaded data
|
|
return retValue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
CloseSignonFile();
|
|
dataManager.RemoveAllData(); // clean up partial loaded data
|
|
|
|
return retValue;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// Now close the signon file
|
|
CloseSignonFile();
|
|
|
|
// Print data for cross checking
|
|
#ifdef DEBUG
|
|
dataManager.PrintAllRejectHosts();
|
|
dataManager.PrintAllHosts();
|
|
#endif
|
|
return FPM_TRUE;
|
|
|
|
}
|
|
|
|
|
|
int SignonManager::WriteSignonData(char *firefoxProfileDir)
|
|
{
|
|
int retValue;
|
|
char *cryptData = NULL;
|
|
char *signonFilePath = NULL;
|
|
char *tempFilePath = NULL;
|
|
char fileName[256];
|
|
|
|
Host *t;
|
|
HostElement *h;
|
|
RejectHost *r;
|
|
|
|
// TODO : If signon data has not changed since last write then return...
|
|
/* // There may be requirement to write empty data...
|
|
if( dataManager.hostList == NULL )
|
|
{
|
|
PrintMessage(MESG_ERROR, "\n WriteSignonData : Signon data is empty...");
|
|
return FPM_SIGNON_DATASTORE_EMPTY;
|
|
}
|
|
*/
|
|
|
|
// Generate random file name
|
|
srand( (unsigned)time( NULL ) );
|
|
sprintf(fileName,"signon_fpm_%d.txt", rand());
|
|
|
|
// Setup the signon and temp filename..
|
|
signonFilePath = (char*) malloc( strlen(firefoxProfileDir) + strlen(SIGNON_FILE_NAME) + 3);
|
|
tempFilePath =(char*) malloc( strlen(firefoxProfileDir) + strlen(fileName) + 3);
|
|
|
|
if( signonFilePath == NULL || tempFilePath == NULL)
|
|
{
|
|
PrintMessage(MESG_ERROR, "\n WriteSignonData : Insufficient memory ....");
|
|
return FPM_INSUFFICIENT_MEMORY;
|
|
}
|
|
|
|
strcpy(signonFilePath, firefoxProfileDir);
|
|
strcat(signonFilePath, "/");
|
|
strcat(signonFilePath, SIGNON_FILE_NAME);
|
|
|
|
strcpy(tempFilePath, firefoxProfileDir);
|
|
strcat(tempFilePath, "/");
|
|
strcat(tempFilePath, fileName);
|
|
|
|
// Open signon file
|
|
if( (retValue = OpenSignonFile(firefoxProfileDir, fileName, "w")) != FPM_TRUE )
|
|
{
|
|
PrintMessage(MESG_ERROR, "\nWriteSignonData : Failed to create temp signon file : %s.", fileName);
|
|
return retValue;
|
|
}
|
|
|
|
|
|
// write out the format revision number
|
|
if( (retValue = WriteLine(HEADER_VERSION)) != FPM_TRUE)
|
|
goto write_signon_error;
|
|
|
|
// write out reject host list
|
|
for(r=dataManager.rejectHostList; r ; r=r->next)
|
|
{
|
|
if( (retValue = WriteLine(r->hostName)) != FPM_TRUE )
|
|
goto write_signon_error;
|
|
|
|
}
|
|
|
|
// End of reject host list
|
|
if( (retValue = WriteLine(".")) != FPM_TRUE )
|
|
goto write_signon_error;
|
|
|
|
|
|
/* format for cached logins shall be:
|
|
* url LINEBREAK {name LINEBREAK value LINEBREAK}* . LINEBREAK
|
|
* if type is password, name is preceded by an asterisk (*)
|
|
*/
|
|
|
|
|
|
// write out each URL node
|
|
for(t=dataManager.hostList; t ; t=t->next)
|
|
{
|
|
PrintMessage(MESG_DEBUG, "\n\nWriteSignonData : Adding name/value pairs for host %s", t->hostName);
|
|
|
|
if( (retValue = WriteLine(t->hostName)) != FPM_TRUE )
|
|
goto write_signon_error;
|
|
|
|
|
|
for(h=t->child; h ; h= h->next)
|
|
{
|
|
PrintMessage(MESG_DEBUG, "\n nWriteSignonData : %s : %s ", h->name, h->value);
|
|
|
|
if( h->isPassword)
|
|
{
|
|
if( (retValue = WriteCharUTF8('*')) != FPM_TRUE )
|
|
goto write_signon_error;
|
|
}
|
|
|
|
if( (retValue = WriteLine(h->name)) != FPM_TRUE )
|
|
goto write_signon_error;
|
|
|
|
|
|
// encrypt the value
|
|
retValue = FPM_FALSE;
|
|
retValue = cryptManager.EncryptString(h->value, &cryptData);
|
|
|
|
if( (retValue == FPM_TRUE) && (cryptData != NULL ))
|
|
{
|
|
if( (retValue = WriteLine(cryptData)) != FPM_TRUE )
|
|
goto write_signon_error;
|
|
}
|
|
else
|
|
{
|
|
PrintMessage(MESG_ERROR, "\n nWriteSignonData : Encryption of value %s failed ", h->value);
|
|
goto write_signon_error;
|
|
}
|
|
}
|
|
|
|
if( (retValue = WriteLine(".")) != FPM_TRUE )
|
|
goto write_signon_error;
|
|
}
|
|
|
|
// close this temporary file...
|
|
CloseSignonFile();
|
|
|
|
|
|
PrintMessage(MESG_DEBUG, "\n WriteSignonData : Removing previous signon file %s ", signonFilePath);
|
|
|
|
// Delete previous signon file
|
|
if( remove(signonFilePath) != 0 )
|
|
{
|
|
// Either file not present or file is locked...
|
|
PrintMessage(MESG_ERROR, "\n WriteSignonData : Failed to delete signon file : %s ", signonFilePath);
|
|
FILE *sfile= NULL;
|
|
if( (sfile = fopen(signonFilePath,"r") ) != NULL )
|
|
{
|
|
// we are able to open signon file in read only mode => file is present
|
|
fclose(sfile);
|
|
remove(tempFilePath); // delete temporary signon file
|
|
|
|
PrintMessage(MESG_ERROR, "\nWriteSignonData : Signon file is locked ");
|
|
return FPM_SIGNON_FILE_LOCKED;
|
|
}
|
|
|
|
// File is not present ..that's good news , so continue...
|
|
}
|
|
|
|
PrintMessage(MESG_DEBUG, "success \n WriteSignonData : Renaming temp file %s back to signon file %s ", tempFilePath, signonFilePath);
|
|
|
|
// Rename temp file back to signon file
|
|
if( ( retValue = rename(tempFilePath, signonFilePath) ) != 0 )
|
|
{
|
|
PrintMessage(MESG_ERROR, "\n WriteSignonData : Failed to rename the temp file %s back to signon file %s ", tempFilePath, signonFilePath);
|
|
PrintMessage(MESG_ERROR, "\n WriteSignonData : Following error has occured : %d ", retValue);
|
|
|
|
return FPM_FALSE;
|
|
}
|
|
|
|
PrintMessage(MESG_DEBUG, "\n WriteSignonData : Successfully written new signon data ...");
|
|
|
|
return FPM_TRUE;
|
|
|
|
write_signon_error:
|
|
|
|
CloseSignonFile();
|
|
remove(tempFilePath); // remove the temporary signon file....
|
|
|
|
return retValue;
|
|
}
|
|
|
|
|
|
|
|
int SignonManager::RemoveSignonData()
|
|
{
|
|
|
|
return dataManager.RemoveAllData();
|
|
|
|
}
|
|
|
|
|
|
int SignonManager::AddHost(struct Host *host)
|
|
{
|
|
|
|
return dataManager.AddHost(host);
|
|
|
|
}
|
|
|
|
|
|
int SignonManager::ModifyHost(struct Host *host)
|
|
{
|
|
|
|
return dataManager.ModifyHost(host);
|
|
|
|
}
|
|
|
|
|
|
|
|
int SignonManager::RemoveHost(char *hostName)
|
|
{
|
|
return dataManager.RemoveHost(hostName);
|
|
}
|
|
|
|
|
|
|
|
|
|
struct Host * SignonManager::GetHostInfo()
|
|
{
|
|
|
|
return dataManager.hostList;
|
|
}
|
|
|
|
|
|
|
|
|