gecko-dev/security/psm/server/servutil.c

1306 строки
33 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Netscape security libraries.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
#include "serv.h"
#include "secport.h"
#include "collectn.h"
#include "resource.h"
#include "connect.h"
#include "plstr.h"
#include "prprf.h"
#include "base64.h"
#include "textgen.h"
#include "protocolf.h"
#include "p12res.h"
#include "ctrlconn.h"
#include "newproto.h"
#include "messages.h"
#ifdef XP_UNIX
#include "private/pprio.h"
#include <unistd.h>
#define GET_LOCK_FILE_PATH(b) \
sprintf(b, "/tmp/.nsmc-%d-lock", (int)geteuid())
#define GET_CONTROL_SOCK_PATH(b) \
sprintf(b, "/tmp/.nsmc-%d", (int)geteuid())
PRFileDesc *lockfile = NULL;
#endif
#define DEBUG_QUEUES
extern SSMCollection * connections;
/* Dumps a (potentially binary) buffer using SSM_DEBUG.
(We could have used the version in ssltrace.c, but that's
specifically tailored to SSLTRACE. Sigh. */
#define DUMPBUF_LINESIZE 8
void
SSM_DumpBuffer(char *buf, PRIntn len)
{
char hexbuf[DUMPBUF_LINESIZE*3+1];
char chrbuf[DUMPBUF_LINESIZE+1];
static const char *hex = "0123456789abcdef";
PRIntn i = 0, l = 0;
char ch, *c, *h;
hexbuf[DUMPBUF_LINESIZE*3] = '\0';
chrbuf[DUMPBUF_LINESIZE] = '\0';
(void) memset(hexbuf, 0x20, DUMPBUF_LINESIZE*3);
(void) memset(chrbuf, 0x20, DUMPBUF_LINESIZE);
h = hexbuf;
c = chrbuf;
while (i < len)
{
ch = buf[i];
if (l == DUMPBUF_LINESIZE)
{
SSM_DEBUG("%s%s\n", hexbuf, chrbuf);
(void) memset(hexbuf, 0x20, DUMPBUF_LINESIZE*3);
(void) memset(chrbuf, 0x20, DUMPBUF_LINESIZE);
h = hexbuf;
c = chrbuf;
l = 0;
}
/* Convert a character to hex. */
*h++ = hex[(ch >> 4) & 0xf];
*h++ = hex[ch & 0xf];
h++;
/* Put the character (if it's printable) into the character buffer. */
if ((ch >= 0x20) && (ch <= 0x7e))
*c++ = ch;
else
*c++ = '.';
i++; l++;
}
SSM_DEBUG("%s%s\n", hexbuf, chrbuf);
}
PRIntn SSM_ReadThisMany(PRFileDesc * sockID, void * buffer, PRIntn thisMany)
{
int got = 0;
PRIntn total = 0;
while (total < thisMany) {
got = PR_Recv(sockID, (void *)((char *)buffer + total), thisMany-total,
0, PR_INTERVAL_NO_TIMEOUT);
if (got <= 0 ) break;
total += got;
}
/* Clear the PR error if we got EOF in the final read,
as opposed to -1 bytes. */
if (got == 0)
PR_SetError(0, 0);
return total;
}
PRIntn SSM_WriteThisMany(PRFileDesc * sockID, void * buffer, PRIntn thisMany)
{
PRIntn total = 0;
while (total < thisMany) {
PRIntn got;
got = PR_Send(sockID, (void *)((char *)buffer+total), thisMany-total,
0, PR_INTERVAL_NO_TIMEOUT);
if (got < 0) break;
total += got;
}
return total;
}
SECItem * SSM_ConstructMessage(PRUintn size)
{
SECItem * ptr = NULL;
#if 0
SSM_DEBUG("allocating message of size %ld.\n", (long) size);
#endif
ptr = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
if (!ptr) {
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
goto loser;
}
ptr->len = size;
if (size > 0) {
ptr->data = (unsigned char *)PORT_ZAlloc(size);
if (!ptr->data) {
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
goto loser;
}
}
return ptr;
loser:
SSM_FreeMessage(ptr);
return NULL;
}
void SSM_FreeMessage(SECItem * msg)
{
if (!msg) return;
if (msg->data)
PORT_Free(msg->data);
PORT_Free(msg);
}
void
ssm_DrainAndDestroyQueue(SSMCollection **coll)
{
SSMCollection *v;
PRIntn type, len;
char *data;
if ((coll == NULL) || (*coll == NULL))
return;
v = *coll;
/* Drain the collection. Don't block; presumably this is
being done at destruction time. */
while(SSM_RecvQMessage(v, SSM_PRIORITY_ANY,
&type, &len, &data,
PR_FALSE) == PR_SUCCESS)
{
if (data)
PR_Free(data);
}
#if 0
/* NotifyAll on the lock and yield time. */
/* ### mwelch - Causing problems on Linux, and shouldn't even
be here since we don't have the queue locked,
so I'm removing it. */
PR_NotifyAll(v->lock);
#endif
/* Destroy the collection. */
SSM_DestroyCollection(v);
/* Zero out the original member. */
*coll = NULL;
}
void
ssm_DrainAndDestroyChildCollection(SSMCollection **coll)
{
SSMCollection *v;
SSMConnection *conn;
if ((coll == NULL) || (*coll == NULL))
return;
v = *coll;
/* Drain the collection. Don't block; presumably this is
being done at destruction time. */
while(SSM_Dequeue(v, SSM_PRIORITY_ANY, (void **) &conn, PR_FALSE) == PR_SUCCESS) {;}
/* Destroy the collection. */
SSM_DestroyCollection(v);
/* Zero out the original member. */
*coll = NULL;
}
static SSMHashTable *ssmThreads = NULL;
void SSM_ConnectionThreadName(char *buf)
{
PRThread * threadptr = PR_GetCurrentThread();
char *name;
buf[0] = '\0'; /* in case we fail */
if (!ssmThreads) return;
if (SSM_HashFind(ssmThreads, (SSMHashKey) threadptr, (void **) &name)
!= SSM_SUCCESS)
return;
if (name)
PL_strcpy(buf, name);
}
static SSMCollection *logSockets = NULL;
static PRMonitor *logSocketLock = NULL;
/* Add the filedesc to a list of sockets to receive log output */
SSMStatus
SSM_AddLogSocket(PRFileDesc *fd)
{
SSMStatus rv = SSM_SUCCESS;
PR_ASSERT(logSockets);
PR_ASSERT(logSocketLock);
PR_EnterMonitor(logSocketLock);
rv = SSM_Enqueue(logSockets, SSM_PRIORITY_NORMAL, fd);
PR_ExitMonitor(logSocketLock);
if (rv != SSM_SUCCESS)
SSM_DEBUG("SSM_AddLogSocket returned error %d.\n", rv);
return rv;
}
/* Called by a newly created thread, this associates a name with the thread. */
void
SSM_RegisterThread(char *threadName, SSMResource *ptr)
{
SSMStatus rv = SSM_SUCCESS;
PRThread *thr = PR_GetCurrentThread();
char *value;
#ifdef XP_MAC
/*
Each thread, when registering itself using one of the two functions
below, assigns itself as private data at index (thdIndex). This is
necessary because when a thread exits, we want it to delete itself
from the list of threads we need to kill at exit time.
*/
{
PRUintn threadIndex = GetThreadIndex();
if (threadIndex > 0) {
if (PR_GetThreadPrivate(threadIndex) != thr)
PR_SetThreadPrivate(threadIndex, thr);
}
}
#endif
if (ptr)
{
PR_ASSERT(SSM_IsAKindOf(ptr, SSM_RESTYPE_RESOURCE));
/* Increment the thread count for this resource. */
SSM_LockResource(ptr);
ptr->m_threadCount++;
SSM_UnlockResource(ptr);
}
if (threadName)
{
if (ptr)
value = PR_smprintf("%s %p", threadName, ptr);
else
value = PL_strdup(threadName);
}
else
value = PR_smprintf("unknown %p/%p", thr, ptr);
if ((!ssmThreads) &&
((rv = SSM_HashCreate(&ssmThreads)) != SSM_SUCCESS))
return;
if (!value) return;
SSM_HashInsert(ssmThreads, (SSMHashKey) thr, value);
}
/* Called by a newly created thread, this associates a name with the thread. */
void
SSM_RegisterNewThread(char *threadName, SSMResource *ptr)
{
SSMStatus rv = SSM_SUCCESS;
PRThread *thr = PR_GetCurrentThread();
char *value;
if (threadName)
{
if (ptr)
value = PR_smprintf("%s %p", threadName, ptr);
else
value = PL_strdup(threadName);
}
else
value = PR_smprintf("unknown %p/%p", thr, ptr);
if ((!ssmThreads) &&
((rv = SSM_HashCreate(&ssmThreads)) != SSM_SUCCESS))
return;
if (!value) return;
SSM_HashInsert(ssmThreads, (SSMHashKey) thr, value);
}
void SSM_Debug(SSMResource *conn, char *msg)
{
#ifdef DEBUG
char name[256];
SSM_ConnectionThreadName(name);
printf("%s: %s", name, msg);
fflush(stdout);
PR_Free(msg);
#endif
}
void SSM_DebugP(char *fmt, ...)
{
#if defined(DEBUG)
char *tmp = NULL, *tmp2 = NULL;
char *timeStamp = NULL;
PRFileDesc *sock = NULL;
PRIntn len;
PRIntn numWritten;
char name[256];
va_list argp;
PRIntn count, i;
PRUint32 rv = 0;
PRExplodedTime exploded;
/* protect against another socket add */
PR_EnterMonitor(logSocketLock);
if (!logSockets)
goto done;
count = SSM_Count(logSockets);
if (count == 0)
goto done;
va_start(argp, fmt);
tmp = PR_vsmprintf(fmt, argp);
va_end(argp);
if (!tmp)
goto done;
PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &exploded);
timeStamp = PR_smprintf("%02i:%02i:%02i.%03i", exploded.tm_hour,
exploded.tm_min, exploded.tm_sec,
exploded.tm_usec/1000);
SSM_ConnectionThreadName(name);
tmp2 = PR_smprintf("[%s] %s: %s", timeStamp, name, tmp);
if (!tmp2)
goto done;
len = strlen(tmp2);
count = SSM_Count(logSockets);
/* count backwards in case we get an error */
for(i=(count-1);i>=0;i--)
{
numWritten = 0;
sock = (PRFileDesc *) SSM_At(logSockets, i);
if (sock)
numWritten = PR_Write(sock, tmp2, len);
if (numWritten < len)
{
/*SSM_Remove(logSockets, sock);*/
/*PR_Close(sock);*/
/*PR_Shutdown(sock, PR_SHUTDOWN_BOTH);*/
rv = 0;
}
}
done:
if (tmp)
PR_smprintf_free(tmp);
if (tmp2)
PR_smprintf_free(tmp2);
if (timeStamp)
PR_smprintf_free(timeStamp);
PR_ExitMonitor(logSocketLock);
#endif
}
#ifdef DEBUG
void
SSM_InitLogging(void)
{
char *suppressConsole = NULL;
char *debugFile = NULL;
/* Create the condition variable we use to control logging. */
if (!(logSocketLock = PR_NewMonitor()))
goto loser;
if (!(logSockets = SSM_NewCollection()))
goto loser;
suppressConsole = PR_GetEnv(SSM_ENV_SUPPRESS_CONSOLE);
debugFile = PR_GetEnv(SSM_ENV_LOG_FILE);
if (!suppressConsole)
{
PRFileDesc *stdfd = PR_GetSpecialFD(PR_StandardOutput);
if (!stdfd)
stdfd = PR_GetSpecialFD(PR_StandardError);
if (stdfd)
SSM_AddLogSocket(stdfd);
}
if (debugFile)
{
PRFileDesc *fd = PR_Open(debugFile,
PR_WRONLY | PR_APPEND |
PR_CREATE_FILE | PR_SYNC,
0644);
if (fd)
SSM_AddLogSocket(fd);
}
SSM_DEBUG("Started logging.\n");
return;
loser:
fprintf(stderr, "Can't initialize logging! Exiting.\n");
exit(1);
}
#endif
SSMStatus SSM_SendQMessage(SSMCollection *q,
PRIntn priority,
PRIntn type, PRIntn length, char *data,
PRBool doBlock)
{
SECItem *n = NULL;
unsigned char *c = NULL;
SSMStatus rv = PR_FAILURE;
PR_ASSERT(priority != SSM_PRIORITY_ANY);
#ifdef DEBUG_QUEUES
SSM_DEBUG("SendQMessage on %lx: prio %d, type %lx, len %d\n",
(unsigned long) q, priority, (unsigned long) type, length);
#endif
/* Create a new message. */
n = (SECItem *) PORT_ZAlloc(sizeof(SECItem));
if (!n)
goto loser;
if (data && length > 0) {
c = (unsigned char *) PORT_ZAlloc(length);
if (!c)
goto loser;
memcpy(c, data, length);
}
n->type = (SECItemType) type;
n->len = length;
n->data = c;
/* ### mwelch We'll want to put a throttle here when we
start really securing Cartman. It's currently
possible for someone to make us run out of
memory very quickly by flooding us with data. */
/* Put the msg in the queue. */
SSM_Enqueue(q, priority, n);
return PR_SUCCESS;
loser:
if (n) PORT_Free(n);
if (c) PORT_Free(c);
return rv;
}
SSMStatus SSM_RecvQMessage(SSMCollection *q,
PRIntn priority,
PRIntn *type, PRIntn *length, char **data,
PRBool doBlock)
{
SECItem *p;
void *v;
#ifdef DEBUG_QUEUES
SSM_DEBUG("RecvQMessage on %lx: %s read at prio %d\n",
(unsigned long) q,
(doBlock ? "blocking" : "nonblocking"),
priority);
#endif
/* Remove the item at the head of the queue. */
SSM_Dequeue(q, priority, &v, doBlock);
p = (SECItem *) v;
if (!p)
return PR_FAILURE;
/* Strip out the items. */
*data = (char *)p->data;
*length = p->len;
*type = p->type;
#ifdef DEBUG_QUEUES
SSM_DEBUG("RecvQMessage on %lx: type %lx, len %d\n",
(unsigned long) q, (unsigned long) *type,
*length);
#endif
/* Free the containing SECItem. */
PORT_Free(p);
return PR_SUCCESS;
}
SSMStatus
SSM_SECItemToSSMString(void ** ssmstring, SECItem * data)
{
SSMString * result = NULL;
if (!ssmstring || !data || data->len <= 0 )
goto loser;
*ssmstring = NULL; /* in case we fail */
result = (SSMString *) SSMPORT_ZAlloc(sizeof(data->len) +
SSMSTRING_PADDED_LENGTH(data->len));
if (!result)
goto loser;
memcpy((char *)(&(result->m_data)), data->data, data->len);
result->m_length = data->len;
*ssmstring = result;
return PR_SUCCESS;
loser:
if (result)
PR_Free(result);
return PR_FAILURE;
}
SSMStatus
SSM_CopyCMTItem(CMTItem * dest, CMTItem * source)
{
SSMStatus rv = SSM_FAILURE;
if (!dest || !source)
goto loser;
dest->len = source->len;
if (!dest->len)
goto done;
dest->data = (unsigned char *) PR_Malloc(dest->len);
if (!dest->data)
goto loser;
memcpy(dest->data, source->data, dest->len);
done:
rv = SSM_SUCCESS;
loser:
return rv;
}
PRFileDesc * SSM_OpenControlPort(void)
{
PRFileDesc * datasocket = NULL;
PRNetAddr servaddr;
SSMStatus status;
PRSocketOptionData sockOpData;
#ifdef XP_UNIX
char lock_name_buf[32];
#endif
/* disable Nagle algorithm delay for control sockets */
sockOpData.option = PR_SockOpt_NoDelay;
sockOpData.value.no_delay = PR_TRUE;
#ifdef XP_UNIX
GET_LOCK_FILE_PATH(lock_name_buf);
lockfile = PR_Open(lock_name_buf, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE,
0600);
if (!lockfile)
goto loser;
status = PR_TLockFile(lockfile);
if (status != PR_SUCCESS)
goto loser;
datasocket = PR_Socket(AF_UNIX, SOCK_STREAM, 0);
if (!datasocket)
goto loser;
servaddr.local.family = AF_UNIX;
GET_CONTROL_SOCK_PATH(servaddr.local.path);
PR_Delete(servaddr.local.path);
#else
datasocket = PR_NewTCPSocket();
if (!datasocket)
goto loser;
status = PR_SetSocketOption(datasocket, &sockOpData);
if (status != PR_SUCCESS) {
goto loser;
}
/* Bind to PSM port on loopback address: connections from non-localhosts
* will be disallowed
*/
status = PR_InitializeNetAddr(PR_IpAddrLoopback, (PRUint16) PSM_PORT,
&servaddr);
if (status != PR_SUCCESS)
goto loser;
#endif
status = PR_Bind(datasocket, &servaddr);
if (status != PR_SUCCESS)
goto loser;
status = PR_Listen(datasocket, MAX_CONNECTIONS);
if (status != PR_SUCCESS)
goto loser;
#ifdef XP_UNIX
SSM_DEBUG("Ready for client connection on port %s.\n", servaddr.local.path);
#else
SSM_DEBUG("Ready for client connection on port %d.\n", PSM_PORT);
#endif
return datasocket;
loser:
return NULL;
}
/* Make sure that the peer is on the same machine that we are */
PRBool
SSM_SocketPeerCheck(PRFileDesc *sock, PRBool isCtrl)
{
PRBool rv = PR_FALSE;
SSMStatus st = PR_SUCCESS;
PRNetAddr theirAddr;
char localaddr[] = {0x7F,0x00,0x00,0x01};/* 127.0.0.1 in network order */
#if defined(XP_UNIX) || defined(XP_MAC)
#ifdef XP_UNIX
if (isCtrl)
{
/* unix domain socket == same machine */
/* ### mwelch Well, the only time this isn't true is when
one wants to join many unix machines in a cluster, but
even in that case the cluster should be treated as a
single machine anyway. */
#endif
/*
GetPeerName isn't implemented on the Mac, so say yes and hope/pray for now
### mwelch - Need to implement GetPeerName in Mac NSPR before Mac Cartman RTM
*/
rv = PR_TRUE;
goto done;
#ifdef XP_UNIX
}
#endif
#endif
st = PR_GetPeerName(sock, &theirAddr);
if (st != PR_SUCCESS)
goto done; /* default is to fail */
/* Compare against localhost IP */
if (!memcmp(&(theirAddr.inet.ip), localaddr, 4))
rv = PR_TRUE;
if (rv != PR_TRUE)
SSM_DEBUG("Failed IP address check!\n");
done:
return rv;
}
PRFileDesc * SSM_OpenPort(void)
{
PRFileDesc * datasocket = NULL;
PRNetAddr servaddr;
SSMStatus status;
PRSocketOptionData sockOpData;
/* disable Nagle algorithm delay for data sockets */
sockOpData.option = PR_SockOpt_NoDelay;
sockOpData.value.no_delay = PR_TRUE;
datasocket = PR_NewTCPSocket();
if (!datasocket)
goto loser;
status = PR_SetSocketOption(datasocket, &sockOpData);
if (status != PR_SUCCESS) {
goto loser;
}
/* Bind to PSM port on loopback address: connections from non-localhosts
* will be disallowed
*/
status = PR_InitializeNetAddr(PR_IpAddrLoopback, 0, &servaddr);
if (status != PR_SUCCESS)
goto loser;
status = PR_Bind(datasocket, &servaddr);
if (status != PR_SUCCESS)
goto loser;
status = PR_Listen(datasocket, MAX_CONNECTIONS);
if (status != PR_SUCCESS)
goto loser;
return datasocket;
loser:
return NULL;
}
#if 0
/* Whee. Base 64 decoding. Have to do it for basic authentication. */
#define XX 127
/*
* Table for decoding base64. We have this everywhere else, why not here.
*/
static char index64[256] = {
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63,
52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX,
XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
};
#ifdef XP_OS2_HACK
/*DSR102196 - the OS/2 Visual Age compiler croaks when it tries*/
/*to optomize this macro (/O+ on CSD4) */
char CHAR64(int c)
{
unsigned char index;
char rc;
index = (unsigned char) c;
rc = index64[index];
return rc;
}
#else /*normal code...*/
#define CHAR64(c) (index64[(unsigned char)(c)])
#endif
/* Decode a base 64 string in place. */
void decode_base64_string(char *str)
{
char *s = str;
char *d = str;
char c[4];
PRIntn i;
PRBool done = PR_FALSE;
while((!done) && (*s))
{
/* Process 4 bytes at a time. */
for(i=0;i<4;i++)
c[i] = 0;
for(i=0;i<4;i++)
{
if ((IS_WHITESPACE(*s)) || (IS_EOL(*s)))
{
/* End here, because we're out of bytes. */
done = PR_TRUE;
}
else
c[i] = *s++;
}
/* Make sure we can at least decode one character. */
if ((!done) && ((c[0] == '=') || (c[1] == '=')))
done = PR_TRUE; /* don't even have enough for one character, leave */
if (!done)
{
/* Decode the first character. */
c[0] = CHAR64(c[0]);
c[1] = CHAR64(c[1]);
*d++ = ((c[0]<<2) | ((c[1] & 0x30)>>4));
/* If we have data for a second character, decode that. */
if (c[2] != '=')
{
c[2] = CHAR64(c[2]);
*d++ = (((c[1] & 0x0F) << 4) | ((c[2] & 0x3C) >> 2));
/* If we have data for a third character, decode that. */
if (c[3] != '=')
{
c[3] = CHAR64(c[3]);
*d++ = (((c[2] & 0x03) << 6) | c[3]);
}
else
done = PR_TRUE;
}
else
done = PR_TRUE;
}
}
/* Terminate the string. */
*d = '\0';
}
#endif
#ifdef DEBUG
char *
SSM_StandaloneGetPasswdCallback(PK11SlotInfo *slot, PRBool retry,
void *arg)
{
char *result = NULL;
char *env = NULL;
SECItem theItem = {siBuffer, NULL, 0};
if (retry)
{
/* Don't spin forever, just bail */
SSM_DEBUG("Static password you provided was not accepted. "
"Cancelling request.\n");
return NULL;
}
env = PR_GetEnv(SSM_ENV_STATIC_PASSWORD);
if (!env)
goto done;
ATOB_ConvertAsciiToItem(&theItem, env);
if (theItem.data == NULL)
goto done;
result = (char *) PR_CALLOC(theItem.len + 1);
if (!result)
goto done;
strncpy(result, (char *) theItem.data, theItem.len);
done:
if (theItem.data != NULL)
SECITEM_FreeItem(&theItem, PR_FALSE);
if (result)
SSM_DEBUG("Responding to password request with a password.\n");
else
SSM_DEBUG("Responding to password callback with NULL password.\n");
return result;
}
PRBool
SSM_StandaloneVerifyPasswdCallback(PK11SlotInfo * slot, void * arg)
{
/* yeah, sure, trust me, we're the ones who are sposta be logged in */
return PR_TRUE;
}
#endif
SSMStatus
SSM_CloseSocketWithLinger(PRFileDesc *sock)
{
PRSocketOptionData opt;
SSMStatus rv = PR_SUCCESS;
/*
Set linger for 15 minutes.
### mwelch This time should really depend on what has been written
to the socket. For example, if we've just written 5 megabytes of data
down a 14.4 wire, we should wait considerably longer than 15 minutes.
The point here is that there's no way to determine a constant value
ahead of time. So, 15 minutes. (Note that generally only client sockets
are closed in this way, so we hopefully will avoid major trouble.)
*/
opt.option = PR_SockOpt_Linger;
opt.value.linger.polarity = PR_TRUE;
opt.value.linger.linger = PR_SecondsToInterval(60*15);
rv = PR_SetSocketOption(sock, &opt);
/* Now close the socket. */
if (rv == PR_SUCCESS)
rv = PR_Close(sock);
return rv;
}
char*
SSM_GetCharFromKey(const char *key, const char *locale)
{
SSMTextGenContext *textGenCxt = NULL;
char *asciiString = NULL;
SSMStatus rv;
rv = SSMTextGen_NewContext(NULL, NULL, NULL, NULL, &textGenCxt);
if (rv != PR_SUCCESS) {
goto loser;
}
rv = SSM_FindUTF8StringInBundles(textGenCxt, key,
&asciiString);
if (rv != PR_SUCCESS) {
goto loser;
}
SSMTextGen_DestroyContext(textGenCxt);
return asciiString;
loser:
if (textGenCxt != NULL) {
SSMTextGen_DestroyContext(textGenCxt);
}
if (asciiString != NULL) {
PR_Free(asciiString);
}
return NULL;
}
SSMStatus SSM_RequestFilePathFromUser(SSMResource *res,
const char *promptKey,
const char *fileRegEx,
PRBool getExistingFile)
{
SECItem *filePathRequest = NULL;
char *asciiPrompt = NULL;
SSMStatus rv;
FilePathRequest request;
filePathRequest = SSM_ZNEW(SECItem);
if (filePathRequest == NULL) {
return SSM_FAILURE;
}
asciiPrompt = SSM_GetCharFromKey(promptKey, "ISO_8859-1");
if (asciiPrompt == NULL) {
goto loser;
}
request.resID = res->m_id;
request.prompt = asciiPrompt;
request.getExistingFile = (CMBool) getExistingFile;
request.fileRegEx = fileRegEx;
if (CMT_EncodeMessage(FilePathRequestTemplate, (CMTItem*)filePathRequest, (void*)&request) != CMTSuccess) {
goto loser;
}
filePathRequest->type = (SECItemType) (SSM_EVENT_MESSAGE | SSM_FILE_PATH_EVENT);
SSM_LockResource(res);
rv = SSM_SendQMessage(res->m_connection->m_controlOutQ,
20,
filePathRequest->type,
filePathRequest->len,
(char*)filePathRequest->data,
PR_TRUE);
SECITEM_FreeItem(filePathRequest, PR_TRUE);
SSM_WaitResource(res, PR_INTERVAL_NO_TIMEOUT);
SSM_UnlockResource(res);
return (res->m_fileName == NULL) ? SSM_FAILURE : SSM_SUCCESS;
loser:
if (filePathRequest != NULL) {
SECITEM_FreeItem(filePathRequest, PR_TRUE);
}
return SSM_FAILURE;
}
void SSM_HandleFilePathReply(SSMControlConnection *ctrl,
SECItem *message)
{
SSMStatus rv;
SSMResource *res;
FilePathReply reply;
if (CMT_DecodeMessage(FilePathReplyTemplate, &reply, (CMTItem*)message) != CMTSuccess) {
return;
}
rv = SSMControlConnection_GetResource(ctrl, reply.resID, &res);
if (rv != PR_SUCCESS || res == NULL) {
return;
}
if (reply.filePath != NULL) {
if (reply.filePath[0] == '\0') {
res->m_fileName = NULL;
} else {
res->m_fileName = PL_strdup(reply.filePath);
}
PR_Free(reply.filePath);
} else {
res->m_fileName = NULL;
res->m_buttonType = SSM_BUTTON_CANCEL;
}
SSM_LockResource(res);
SSM_NotifyResource(res);
SSM_UnlockResource(res);
SSM_FreeResource(res);
}
void SSM_HandleUserPromptReply(SSMControlConnection *ctrl,
SECItem *msg)
{
SSMStatus rv;
SSMResource *res;
PromptReply reply;
if (CMT_DecodeMessage(PromptReplyTemplate, &reply, (CMTItem*)msg) != CMTSuccess) {
return;
}
rv = SSMControlConnection_GetResource(ctrl, reply.resID, &res);
if (rv != PR_SUCCESS || res == NULL) {
return;
}
/* XXX sjlee: if the password length was zero, it would have been
* translated as a null pointer through transport. We need to restore it
* to an empty string. We can do that by looking up the cancel value of
* the reply.
*/
if ((reply.promptReply == NULL) && !reply.cancel) {
reply.promptReply = "";
}
SSM_HandlePromptReply(res, reply.promptReply);
}
#ifdef TIMEBOMB
SECItem * SSMTimebomb_GetMessage(SSMControlConnection * control)
{
SECItem * message = NULL;
PRInt32 read, type, len;
char * buffer = NULL;
SSMStatus rv = PR_FAILURE;
buffer = (char *)PORT_ZAlloc(sizeof(type)+ sizeof(len));
if (!buffer)
goto loser;
SSM_DEBUG("waiting for new message from socket.\n");
read = SSM_ReadThisMany(control->m_socket,(void *)buffer,sizeof(type)+sizeof(len));
if (read != sizeof(type)+sizeof(len))
{
SSM_DEBUG("Bytes read (%ld) != bytes expected (%ld). (hdr)\n",
(long) read,
(long) (sizeof(type)+sizeof(len)));
rv = PR_FAILURE;
goto loser;
}
message = SSM_ConstructMessage(PR_ntohl(((unsigned long *)buffer)[1]));
if (!message || !message->data)
{
SSM_DEBUG("Missing %s.\n",(!message) ? "message" : "message data");
rv = PR_OUT_OF_MEMORY_ERROR;
goto loser;
}
message->type = PR_ntohl(((unsigned long *)buffer)[0]);
SSM_DEBUG("reading %ld more from socket.\n", message->len);
read = SSM_ReadThisMany(control->m_socket, (void *)message->data, message->len);
if ((unsigned int) read != message->len)
{
SSM_DEBUG("Bytes read (%ld) != bytes expected (%ld). (msg)\n",
(long) read,
(long) message->len);
rv = PR_GetError();
if (rv == PR_SUCCESS) rv = PR_FAILURE;
goto loser;
}
if (buffer)
PR_Free(buffer);
return message;
loser:
if (buffer)
PR_Free(buffer);
return NULL;
}
SSMStatus SSMTimebomb_SendMessage(SSMControlConnection * control,
SECItem * message)
{
PRInt32 tmp, sent;
if (!message)
goto loser;
/* now send message.
* I want to do it here to keep timebomb code close together -jane */
tmp = PR_htonl(message->type);
sent = SSM_WriteThisMany(control->m_socket, &tmp, sizeof(tmp));
if (sent != sizeof(tmp))
goto loser;
tmp = PR_htonl(message->len);
sent = SSM_WriteThisMany(control->m_socket, &tmp, sizeof(tmp));
if (sent != sizeof(tmp))
goto loser;
sent = SSM_WriteThisMany(control->m_socket, message->data, message->len);
if (sent != message->len)
goto loser;
return SSM_SUCCESS;
loser:
SSM_DEBUG("timebomb: can't send error message!\n");
return SSM_FAILURE;
}
#endif
void SSM_DestroyAttrValue(SSMAttributeValue *value, PRBool freeit)
{
if ((value->type == SSM_STRING_ATTRIBUTE) && (value->u.string.data))
PR_Free(value->u.string.data);
value->type = (SSMResourceAttrType) 0;
if (freeit)
PR_Free(value);
}
CMTStatus SSM_SSMStringToString(char ** string,
int *len,
SSMString * ssmString)
{
char * str = NULL;
int realLen;
CMTStatus rv = CMTSuccess;
if (!ssmString || !string ) {
rv = (CMTStatus) PR_INVALID_ARGUMENT_ERROR;
goto loser;
}
/* in case we fail */
*string = NULL;
if (len) *len = 0;
/* Convert from net byte order */
realLen = SSMPR_ntohl(ssmString->m_length);
str = (char *)PR_CALLOC(realLen+1); /* add 1 byte for end 0 */
if (!str) {
rv = (CMTStatus) PR_OUT_OF_MEMORY_ERROR;
goto loser;
}
memcpy(str, (char *) &(ssmString->m_data), realLen);
/* str[realLen]=0; */
if (len) *len = realLen;
*string = str;
return rv;
loser:
if (str)
PR_Free(str);
if (string && *string) {
PR_Free(*string);
*string = NULL;
}
if (rv == CMTSuccess)
rv = CMTFailure;
return rv;
}
CMTStatus SSM_StringToSSMString(SSMString ** ssmString, int length,
char * string)
{
SSMPRUint32 len;
SSMString *result = NULL;
CMTStatus rv = CMTSuccess;
if (!string || !ssmString) {
rv = (CMTStatus) PR_INVALID_ARGUMENT_ERROR;
goto loser;
}
*ssmString = NULL; /* in case we fail */
if (length) len = length;
else len = strlen(string);
if (len <= 0) {
rv = (CMTStatus) PR_INVALID_ARGUMENT_ERROR;
goto loser;
}
result = (SSMString *) PR_CALLOC(sizeof(PRUint32) +
SSMSTRING_PADDED_LENGTH(len));
if (!result) {
rv = (CMTStatus) PR_OUT_OF_MEMORY_ERROR;
goto loser;
}
result->m_length = SSMPR_htonl(len);
memcpy((char *) (&(result->m_data)), string, len);
*ssmString = result;
goto done;
loser:
if (result)
PR_Free(result);
*ssmString = NULL;
if (rv == CMTSuccess)
rv = CMTFailure;
done:
return rv;
}
#ifdef XP_UNIX
void SSM_ReleaseLockFile()
{
if (lockfile) {
char filePath[64];
GET_LOCK_FILE_PATH(filePath);
SSM_DEBUG("Releasing and deleting the file %s\n", filePath);
PR_UnlockFile(lockfile);
PR_Close(lockfile);
lockfile = NULL;
PR_Delete(filePath);
GET_CONTROL_SOCK_PATH(filePath);
SSM_DEBUG("Deleting %s. (The control connection socket)\n", filePath);
PR_Delete(filePath);
}
}
#endif
int SSM_strncasecmp(const char *s1, const char *s2, size_t count)
{
char *myS1 = NULL, *myS2 = NULL;
int rv, len, i;
myS1 = strdup(s1);
myS2 = strdup(s2);
len = strlen(myS1);
for (i=0;i<len;i++) {
myS1[i] = tolower(myS1[i]);
}
len = strlen(myS2);
for (i=0;i<len;i++) {
myS2[i] = tolower(myS2[i]);
}
rv = strncmp(myS1,myS2,count);
free(myS1);
free(myS2);
return rv;
}