603 lines
17 KiB
C
603 lines
17 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 <string.h>
|
|
#include <assert.h>
|
|
#ifdef WIN32
|
|
#include <winsock.h>
|
|
#endif
|
|
#ifdef XP_MAC
|
|
#include "macsocket.h"
|
|
#endif
|
|
|
|
#include "newproto.h"
|
|
|
|
char SSMVersionString[] = "1.1";
|
|
|
|
CMT_Alloc_fn cmt_alloc = malloc;
|
|
CMT_Free_fn cmt_free = free;
|
|
|
|
#define ASSERT(x) assert(x)
|
|
|
|
#define CM_ntohl ntohl
|
|
#define CM_htonl htonl
|
|
|
|
|
|
/*************************************************************
|
|
*
|
|
* CMT_Init
|
|
*
|
|
*
|
|
************************************************************/
|
|
void
|
|
CMT_Init(CMT_Alloc_fn allocfn, CMT_Free_fn freefn)
|
|
{
|
|
cmt_alloc = allocfn;
|
|
cmt_free = freefn;
|
|
}
|
|
|
|
static CMTStatus
|
|
decode_int(unsigned char **curptr, void *dest, CMInt32 *remaining)
|
|
{
|
|
CMInt32 datalen = sizeof(CMInt32);
|
|
|
|
if (*remaining < datalen)
|
|
return CMTFailure;
|
|
*(CMInt32 *)dest = ntohl(**(CMInt32 **)curptr);
|
|
*remaining -= datalen;
|
|
*curptr += datalen;
|
|
return CMTSuccess;
|
|
}
|
|
|
|
static CMTStatus
|
|
decode_string(unsigned char **curptr, CMInt32 *len,
|
|
unsigned char **data, CMInt32 *remaining)
|
|
{
|
|
CMTStatus rv;
|
|
CMInt32 datalen;
|
|
|
|
rv = decode_int(curptr, len, remaining);
|
|
if (rv != CMTSuccess)
|
|
return CMTFailure;
|
|
|
|
/* NULL string */
|
|
if (*len == 0) {
|
|
*data = NULL;
|
|
goto done;
|
|
}
|
|
|
|
datalen = (*len + 3) & ~3;
|
|
if (*remaining < datalen)
|
|
return CMTFailure;
|
|
|
|
*data = (unsigned char *) cmt_alloc(*len + 1);
|
|
if (*data == NULL)
|
|
return CMTFailure;
|
|
|
|
memcpy(*data, *curptr, *len);
|
|
(*data)[*len] = 0;
|
|
*remaining -= datalen;
|
|
*curptr += datalen;
|
|
done:
|
|
return CMTSuccess;
|
|
}
|
|
|
|
/*************************************************************
|
|
* CMT_DecodeMessage
|
|
*
|
|
* Decode msg into dest as specified by tmpl.
|
|
*
|
|
************************************************************/
|
|
CMTStatus
|
|
CMT_DecodeMessage(CMTMessageTemplate *tmpl, void *dest, CMTItem *msg)
|
|
{
|
|
unsigned char *curptr, *destptr, *list;
|
|
void ** ptr;
|
|
CMInt32 remaining, len, choiceID = 0, listSize, listCount = 0;
|
|
CMBool inChoice = CM_FALSE, foundChoice = CM_FALSE, inList = CM_FALSE;
|
|
CMInt32 listItemSize = 0;
|
|
CMTStatus rv = CMTSuccess;
|
|
CMTMessageTemplate *startOfList, *p;
|
|
CMBool inStructList = CM_FALSE;
|
|
|
|
curptr = msg->data;
|
|
remaining = msg->len;
|
|
|
|
while(tmpl->type != CMT_DT_END) {
|
|
/* XXX Maybe this should be a more formal state machine? */
|
|
if (inChoice) {
|
|
if (tmpl->type == CMT_DT_END_CHOICE) {
|
|
if (!foundChoice)
|
|
goto loser;
|
|
inChoice = CM_FALSE;
|
|
foundChoice = CM_FALSE;
|
|
tmpl++;
|
|
continue;
|
|
}
|
|
if (choiceID != tmpl->choiceID) {
|
|
tmpl++;
|
|
continue; /* Not this option */
|
|
} else {
|
|
foundChoice = CM_TRUE;
|
|
}
|
|
}
|
|
if (inList) {
|
|
destptr = &list[listCount * listItemSize];
|
|
listCount++;
|
|
} else {
|
|
if (inStructList) {
|
|
destptr = tmpl->offset + list;
|
|
} else {
|
|
destptr = tmpl->offset + (unsigned char *)dest;
|
|
}
|
|
}
|
|
switch (tmpl->type) {
|
|
case CMT_DT_RID:
|
|
case CMT_DT_INT:
|
|
case CMT_DT_BOOL:
|
|
rv = decode_int(&curptr, destptr, &remaining);
|
|
if (rv != CMTSuccess)
|
|
goto loser;
|
|
break;
|
|
case CMT_DT_STRING:
|
|
rv = decode_string(&curptr, &len, (unsigned char **)destptr,
|
|
&remaining);
|
|
if (rv != CMTSuccess)
|
|
goto loser;
|
|
break;
|
|
case CMT_DT_ITEM:
|
|
rv = decode_string(&curptr, (long *) &((CMTItem *)destptr)->len,
|
|
&((CMTItem *)destptr)->data, &remaining);
|
|
if (rv != CMTSuccess)
|
|
goto loser;
|
|
break;
|
|
case CMT_DT_LIST:
|
|
/* XXX This is too complicated */
|
|
rv = decode_int(&curptr, destptr, &remaining);
|
|
if (rv != CMTSuccess)
|
|
goto loser;
|
|
listSize = *(CMInt32 *)destptr;
|
|
tmpl++;
|
|
if (tmpl->type == CMT_DT_STRING) {
|
|
listItemSize = sizeof(unsigned char *);
|
|
} else if (tmpl->type == CMT_DT_ITEM) {
|
|
listItemSize = sizeof(CMTItem);
|
|
} else {
|
|
listItemSize = sizeof(CMInt32);
|
|
}
|
|
if (listSize == 0) {
|
|
list = NULL;
|
|
} else {
|
|
list = (unsigned char *) cmt_alloc(listSize * listItemSize);
|
|
}
|
|
*(void **)(tmpl->offset + (unsigned char *)dest) = list;
|
|
inList = CM_TRUE;
|
|
listCount = 0;
|
|
break;
|
|
case CMT_DT_STRUCT_LIST:
|
|
/* XXX This is too complicated */
|
|
rv = decode_int(&curptr, destptr, &remaining);
|
|
if (rv != CMTSuccess)
|
|
goto loser;
|
|
listSize = *(CMInt32 *)destptr;
|
|
tmpl++;
|
|
if (tmpl->type != CMT_DT_STRUCT_PTR) {
|
|
goto loser;
|
|
}
|
|
ptr = (void**)(tmpl->offset + (unsigned char *)dest);
|
|
startOfList = tmpl;
|
|
p = tmpl;
|
|
listItemSize = 0;
|
|
while (p->type != CMT_DT_END_STRUCT_LIST) {
|
|
if (p->type == CMT_DT_STRING) {
|
|
listItemSize += sizeof(unsigned char *);
|
|
} else if (p->type == CMT_DT_ITEM) {
|
|
listItemSize += sizeof(CMTItem);
|
|
} else if (p->type == CMT_DT_INT) {
|
|
listItemSize += sizeof(CMInt32);
|
|
}
|
|
p++;
|
|
}
|
|
if (listSize == 0) {
|
|
list = NULL;
|
|
} else {
|
|
list = (unsigned char *) cmt_alloc(listSize * listItemSize);
|
|
}
|
|
*ptr = list;
|
|
inStructList = CM_TRUE;
|
|
listCount = 0;
|
|
break;
|
|
case CMT_DT_END_STRUCT_LIST:
|
|
listCount++;
|
|
if (listCount == listSize) {
|
|
inStructList = CM_FALSE;
|
|
} else {
|
|
list += listItemSize;
|
|
tmpl = startOfList;
|
|
}
|
|
break;
|
|
case CMT_DT_CHOICE:
|
|
rv = decode_int(&curptr, destptr, &remaining);
|
|
if (rv != CMTSuccess)
|
|
goto loser;
|
|
choiceID = *(CMInt32 *)destptr;
|
|
inChoice = CM_TRUE;
|
|
foundChoice = CM_FALSE;
|
|
break;
|
|
case CMT_DT_END_CHOICE: /* Loop should exit before we see these. */
|
|
case CMT_DT_END:
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
if (inList) {
|
|
if (listCount == listSize) {
|
|
inList = CM_FALSE;
|
|
tmpl++;
|
|
}
|
|
} else {
|
|
tmpl++;
|
|
}
|
|
}
|
|
loser:
|
|
/* Free the data buffer */
|
|
if (msg->data) {
|
|
cmt_free(msg->data);
|
|
msg->data = NULL;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
static CMTStatus
|
|
calc_msg_len(CMTMessageTemplate *tmpl, void *src, CMInt32 *len_out)
|
|
{
|
|
CMInt32 len = 0, choiceID = 0, listSize, listItemSize, listCount;
|
|
unsigned char *srcptr, *list;
|
|
CMBool inChoice = CM_FALSE, inList = CM_FALSE, foundChoice = CM_FALSE;
|
|
CMTMessageTemplate *startOfList, *p;
|
|
CMBool inStructList = CM_FALSE;
|
|
|
|
while(tmpl->type != CMT_DT_END) {
|
|
if (inChoice) {
|
|
if (tmpl->type == CMT_DT_END_CHOICE) {
|
|
if (!foundChoice)
|
|
goto loser;
|
|
inChoice = CM_FALSE;
|
|
foundChoice = CM_FALSE;
|
|
tmpl++;
|
|
continue;
|
|
}
|
|
if (choiceID != tmpl->choiceID) {
|
|
tmpl++;
|
|
continue; /* Not this option */
|
|
} else {
|
|
foundChoice = CM_TRUE;
|
|
}
|
|
}
|
|
if (inList) {
|
|
srcptr = &list[listCount * listItemSize];
|
|
listCount++;
|
|
} else if (inStructList) {
|
|
srcptr = tmpl->offset + list;
|
|
} else {
|
|
srcptr = tmpl->offset + (unsigned char *)src;
|
|
}
|
|
switch(tmpl->type) {
|
|
case CMT_DT_RID:
|
|
case CMT_DT_INT:
|
|
case CMT_DT_BOOL:
|
|
len += sizeof(CMInt32);
|
|
break;
|
|
case CMT_DT_STRING:
|
|
len += sizeof(CMInt32);
|
|
/* Non NULL string */
|
|
if (*(char**)srcptr) {
|
|
len += (strlen(*(char**)srcptr) + 4) & ~3;
|
|
}
|
|
break;
|
|
case CMT_DT_ITEM:
|
|
len += sizeof(CMInt32);
|
|
len += (((CMTItem *)srcptr)->len + 3) & ~3;
|
|
break;
|
|
case CMT_DT_LIST:
|
|
len += sizeof(CMInt32);
|
|
listSize = *(CMInt32 *)srcptr;
|
|
tmpl++;
|
|
if (tmpl->type == CMT_DT_STRING) {
|
|
listItemSize = sizeof(unsigned char *);
|
|
} else if (tmpl->type == CMT_DT_ITEM) {
|
|
listItemSize = sizeof(CMTItem);
|
|
} else {
|
|
listItemSize = sizeof(CMInt32);
|
|
}
|
|
list = *(unsigned char **)(tmpl->offset + (unsigned char *)src);
|
|
listCount = 0;
|
|
inList = CM_TRUE;
|
|
break;
|
|
case CMT_DT_STRUCT_LIST:
|
|
len += sizeof(CMInt32);
|
|
listSize = *(CMInt32 *)srcptr;
|
|
tmpl++;
|
|
if (tmpl->type != CMT_DT_STRUCT_PTR) {
|
|
goto loser;
|
|
}
|
|
list = *(unsigned char**)(tmpl->offset + (unsigned char*)src);
|
|
startOfList = tmpl;
|
|
p = tmpl;
|
|
listItemSize = 0;
|
|
while (p->type != CMT_DT_END_STRUCT_LIST) {
|
|
if (p->type == CMT_DT_STRING) {
|
|
listItemSize += sizeof(unsigned char *);
|
|
} else if (p->type == CMT_DT_ITEM) {
|
|
listItemSize += sizeof(CMTItem);
|
|
} else if (p->type == CMT_DT_INT) {
|
|
listItemSize += sizeof(CMInt32);
|
|
}
|
|
p++;
|
|
}
|
|
listCount = 0;
|
|
inStructList = CM_TRUE;
|
|
break;
|
|
case CMT_DT_END_STRUCT_LIST:
|
|
listCount++;
|
|
if (listCount == listSize) {
|
|
inStructList = CM_FALSE;
|
|
} else {
|
|
list += listItemSize;
|
|
tmpl = startOfList;
|
|
}
|
|
break;
|
|
case CMT_DT_CHOICE:
|
|
len += sizeof(CMInt32);
|
|
choiceID = *(CMInt32 *)srcptr;
|
|
inChoice = CM_TRUE;
|
|
foundChoice = CM_FALSE;
|
|
break;
|
|
case CMT_DT_END_CHOICE: /* Loop should exit before we see these. */
|
|
case CMT_DT_END:
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
if (inList) {
|
|
if (listCount == listSize) {
|
|
inList = CM_FALSE;
|
|
tmpl++;
|
|
}
|
|
} else {
|
|
tmpl++;
|
|
}
|
|
}
|
|
*len_out = len;
|
|
return CMTSuccess;
|
|
loser:
|
|
return CMTFailure;
|
|
}
|
|
|
|
static CMTStatus
|
|
encode_int(unsigned char **curptr, void *src, CMInt32 *remaining)
|
|
{
|
|
CMInt32 datalen = sizeof(CMInt32);
|
|
|
|
if (*remaining < datalen)
|
|
return CMTFailure;
|
|
**(CMInt32 **)curptr = CM_htonl(*(CMInt32 *)src);
|
|
*remaining -= datalen;
|
|
*curptr += datalen;
|
|
return CMTSuccess;
|
|
}
|
|
|
|
static CMTStatus
|
|
encode_string(unsigned char **curptr, CMInt32 len,
|
|
unsigned char *data, CMInt32 *remaining)
|
|
{
|
|
CMTStatus rv;
|
|
CMInt32 datalen;
|
|
|
|
rv = encode_int(curptr, &len, remaining);
|
|
if (rv != CMTSuccess)
|
|
return CMTFailure;
|
|
|
|
/* NULL string */
|
|
if (len == 0) {
|
|
goto done;
|
|
}
|
|
|
|
datalen = (len + 3) & ~3;
|
|
if (*remaining < datalen)
|
|
return CMTFailure;
|
|
|
|
memcpy(*curptr, data, len);
|
|
*remaining -= datalen;
|
|
*curptr += datalen;
|
|
done:
|
|
return CMTSuccess;
|
|
}
|
|
|
|
/*************************************************************
|
|
* CMT_EncodeMessage
|
|
*
|
|
* Encode src into msg as specified by tmpl.
|
|
*
|
|
************************************************************/
|
|
CMTStatus
|
|
CMT_EncodeMessage(CMTMessageTemplate *tmpl, CMTItem *msg, void *src)
|
|
{
|
|
CMInt32 choiceID = 0, listSize, listItemSize, listCount, remaining;
|
|
unsigned char *srcptr, *curptr, *list;
|
|
CMBool inChoice = CM_FALSE, inList = CM_FALSE, foundChoice = CM_FALSE;
|
|
CMTStatus rv = CMTSuccess;
|
|
CMTMessageTemplate *startOfList, *p;
|
|
CMBool inStructList = CM_FALSE;
|
|
|
|
rv = calc_msg_len(tmpl, src, (long *) &msg->len);
|
|
if (rv != CMTSuccess)
|
|
goto loser;
|
|
curptr = msg->data = (unsigned char *) cmt_alloc(msg->len);
|
|
if(msg->data == NULL)
|
|
goto loser;
|
|
remaining = msg->len;
|
|
|
|
while(tmpl->type != CMT_DT_END) {
|
|
if (inChoice) {
|
|
if (tmpl->type == CMT_DT_END_CHOICE) {
|
|
if (!foundChoice)
|
|
goto loser;
|
|
inChoice = CM_FALSE;
|
|
foundChoice = CM_FALSE;
|
|
tmpl++;
|
|
continue;
|
|
}
|
|
if (choiceID != tmpl->choiceID) {
|
|
tmpl++;
|
|
continue; /* Not this option */
|
|
} else {
|
|
foundChoice = CM_TRUE;
|
|
}
|
|
}
|
|
if (inList) {
|
|
srcptr = &list[listCount * listItemSize];
|
|
listCount++;
|
|
} else {
|
|
if (inStructList) {
|
|
srcptr = tmpl->offset + list;
|
|
} else {
|
|
srcptr = tmpl->offset + (unsigned char *)src;
|
|
}
|
|
}
|
|
switch(tmpl->type) {
|
|
case CMT_DT_RID:
|
|
case CMT_DT_INT:
|
|
case CMT_DT_BOOL:
|
|
rv = encode_int(&curptr, srcptr, &remaining);
|
|
if (rv != CMTSuccess)
|
|
goto loser;
|
|
break;
|
|
case CMT_DT_STRING:
|
|
if (*(char**)srcptr) {
|
|
/* Non NULL string */
|
|
rv = encode_string(&curptr, (long) strlen(*(char**)srcptr),
|
|
*(unsigned char**)srcptr, &remaining);
|
|
} else {
|
|
/* NULL string */
|
|
rv = encode_string(&curptr, 0L, *(unsigned char**)srcptr, &remaining);
|
|
}
|
|
if (rv != CMTSuccess)
|
|
goto loser;
|
|
break;
|
|
case CMT_DT_ITEM:
|
|
rv = encode_string(&curptr, ((CMTItem *)srcptr)->len,
|
|
((CMTItem *)srcptr)->data, &remaining);
|
|
if (rv != CMTSuccess)
|
|
goto loser;
|
|
break;
|
|
case CMT_DT_LIST:
|
|
rv = encode_int(&curptr, srcptr, &remaining);
|
|
if (rv != CMTSuccess)
|
|
goto loser;
|
|
listSize = *(CMInt32 *)srcptr;
|
|
tmpl++;
|
|
if (tmpl->type == CMT_DT_STRING) {
|
|
listItemSize = sizeof(unsigned char *);
|
|
} else if (tmpl->type == CMT_DT_ITEM) {
|
|
listItemSize = sizeof(CMTItem);
|
|
} else {
|
|
listItemSize = sizeof(CMInt32);
|
|
}
|
|
list = *(unsigned char **)(tmpl->offset + (unsigned char *)src);
|
|
listCount = 0;
|
|
inList = CM_TRUE;
|
|
break;
|
|
case CMT_DT_STRUCT_LIST:
|
|
rv = encode_int(&curptr, srcptr, &remaining);
|
|
if (rv != CMTSuccess)
|
|
goto loser;
|
|
listSize = *(CMInt32 *)srcptr;
|
|
tmpl++;
|
|
if (tmpl->type != CMT_DT_STRUCT_PTR) {
|
|
goto loser;
|
|
}
|
|
list = *(unsigned char**)(tmpl->offset + (unsigned char*)src);
|
|
startOfList = tmpl;
|
|
p = tmpl;
|
|
listItemSize = 0;
|
|
while (p->type != CMT_DT_END_STRUCT_LIST) {
|
|
if (p->type == CMT_DT_STRING) {
|
|
listItemSize += sizeof(unsigned char *);
|
|
} else if (p->type == CMT_DT_ITEM) {
|
|
listItemSize += sizeof(CMTItem);
|
|
} else if (p->type == CMT_DT_INT) {
|
|
listItemSize += sizeof(CMInt32);
|
|
}
|
|
p++;
|
|
}
|
|
listCount = 0;
|
|
inStructList = CM_TRUE;
|
|
break;
|
|
case CMT_DT_END_STRUCT_LIST:
|
|
listCount++;
|
|
if (listCount == listSize) {
|
|
inStructList = CM_FALSE;
|
|
} else {
|
|
list += listItemSize;
|
|
tmpl = startOfList;
|
|
}
|
|
break;
|
|
case CMT_DT_CHOICE:
|
|
rv = encode_int(&curptr, srcptr, &remaining);
|
|
if (rv != CMTSuccess)
|
|
goto loser;
|
|
choiceID = *(CMInt32 *)srcptr;
|
|
inChoice = CM_TRUE;
|
|
foundChoice = CM_FALSE;
|
|
break;
|
|
case CMT_DT_END_CHOICE: /* Loop should exit before we see these. */
|
|
case CMT_DT_END:
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
if (inList) {
|
|
if (listCount == listSize) {
|
|
inList = CM_FALSE;
|
|
tmpl++;
|
|
}
|
|
} else {
|
|
tmpl++;
|
|
}
|
|
}
|
|
return CMTSuccess;
|
|
loser:
|
|
return CMTFailure;
|
|
}
|