Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve message sanity checks in camessage.c #141

Open
wants to merge 18 commits into
base: 7.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
2ef15ed
camessage.c: Verify that the incoming packets have a valid command ID
jesusvasquez333 Mar 10, 2021
fdb498a
camessage.c: Add note about trying to detection deprecated version
jesusvasquez333 Mar 10, 2021
1bcf55e
camessage.c: do not check if th client version is compatible, before
jesusvasquez333 Mar 11, 2021
1efbb7e
camessage.c: Fix formatting
jesusvasquez333 Mar 11, 2021
c624a31
camessage.c: Break down long comment lines
jesusvasquez333 Mar 11, 2021
9e1a8bf
camessage.c: Verify that incoming packets have a valid payload size
jesusvasquez333 Mar 11, 2021
c8105a1
camessage.c: Add description
jesusvasquez333 Mar 11, 2021
5db7541
camessage.c: Add missing condition to detect extended message form,
jesusvasquez333 Mar 11, 2021
60b5d04
camessage.c: remove extra blank spaces
jesusvasquez333 Mar 11, 2021
7eafd07
camessage.c: Use INVALID_DB_REQ() macro instead of directly compare
jesusvasquez333 Mar 11, 2021
5cc49cf
camessage.c: Fix indentation on event_cancel_reply() function
jesusvasquez333 Mar 11, 2021
29f9d4d
camessage.c: CA_PROTO_EVENT_CANCEL message contain the data type
jesusvasquez333 Mar 11, 2021
f06ce63
camessage.c: Do not disconnect client due to invalid commands. Send
jesusvasquez333 Mar 24, 2021
94e6ec6
camessage.c: revert back client version compatibility check.
jesusvasquez333 Apr 2, 2021
d8dbe96
Checking the payload size for invalid CA messages didn't work with ca…
marciodo May 14, 2022
9ae9318
Merge branch '7.0' into issue128
marciodo May 14, 2022
6e69522
Removed uneeded variable created for the original CA valid message te…
marciodo May 14, 2022
23c8f4a
Removed uneeded spaces and line break
marciodo May 14, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions modules/ca/src/client/caProto.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
# define CA_V411(MINOR) ((MINOR)>=11u) /* sequence numbers in UDP version command */
# define CA_V412(MINOR) ((MINOR)>=12u) /* TCP-based search requests */
# define CA_V413(MINOR) ((MINOR)>=13u) /* Allow zero length in requests. */
# define CA_LAST_MINOR 13u

/*
* These port numbers are only used if the CA repeater and
Expand Down
231 changes: 171 additions & 60 deletions modules/database/src/ioc/rsrv/camessage.c
Original file line number Diff line number Diff line change
Expand Up @@ -1670,7 +1670,7 @@ static int write_notify_action ( caHdrLargeArray *mp, void *pPayload,
return RSRV_ERROR;
}

if (mp->m_dataType > LAST_BUFFER_TYPE) {
if ( INVALID_DB_REQ(mp->m_dataType) ) {
log_header ("bad put notify data type", client, mp, pPayload, 0);
putNotifyErrorReply (client, mp, ECA_BADTYPE);
return RSRV_ERROR;
Expand Down Expand Up @@ -2006,71 +2006,75 @@ static int clear_channel_reply ( caHdrLargeArray *mp,
*/
static int event_cancel_reply ( caHdrLargeArray *mp, void *pPayload, struct client *client )
{
struct channel_in_use *pciu;
struct event_ext *pevext;
int status;
struct channel_in_use *pciu;
struct event_ext *pevext;
int status;

/*
*
* Verify the channel
*
*/
pciu = MPTOPCIU(mp);
if (pciu?pciu->client!=client:TRUE) {
logBadId ( client, mp, pPayload );
return RSRV_ERROR;
}
if ( INVALID_DB_REQ(mp->m_dataType) ) {
return RSRV_ERROR;
}

/*
* search events on this channel for a match
* (there are usually very few monitors per channel)
*/
epicsMutexMustLock(client->eventqLock);
for (pevext = (struct event_ext *) ellFirst(&pciu->eventq);
pevext; pevext = (struct event_ext *) ellNext(&pevext->node)){
/*
*
* Verify the channel
*
*/
pciu = MPTOPCIU(mp);
if (pciu?pciu->client!=client:TRUE) {
logBadId ( client, mp, pPayload );
return RSRV_ERROR;
}

if (pevext->msg.m_available == mp->m_available) {
ellDelete(&pciu->eventq, &pevext->node);
break;
}
}
epicsMutexUnlock(client->eventqLock);
/*
* search events on this channel for a match
* (there are usually very few monitors per channel)
*/
epicsMutexMustLock(client->eventqLock);
for (pevext = (struct event_ext *) ellFirst(&pciu->eventq);
pevext; pevext = (struct event_ext *) ellNext(&pevext->node)){

/*
* Not Found- return an exception event
*/
if(!pevext){
SEND_LOCK(client);
send_err(mp, ECA_BADMONID, client, RECORD_NAME(pciu->dbch));
SEND_UNLOCK(client);
return RSRV_ERROR;
}
if (pevext->msg.m_available == mp->m_available) {
ellDelete(&pciu->eventq, &pevext->node);
break;
}
}
epicsMutexUnlock(client->eventqLock);

/*
* cancel monitor activity in progress
*/
if (pevext->pdbev) {
db_cancel_event (pevext->pdbev);
}
/*
* Not Found- return an exception event
*/
if(!pevext){
SEND_LOCK(client);
send_err(mp, ECA_BADMONID, client, RECORD_NAME(pciu->dbch));
SEND_UNLOCK(client);
return RSRV_ERROR;
}

/*
* send delete confirmed message
*/
SEND_LOCK(client);
/*
* cancel monitor activity in progress
*/
if (pevext->pdbev) {
db_cancel_event (pevext->pdbev);
}

status = cas_copy_in_header ( client, pevext->msg.m_cmmd,
0u, pevext->msg.m_dataType, pevext->msg.m_count, pevext->msg.m_cid,
pevext->msg.m_available, NULL );
if ( status != ECA_NORMAL ) {
SEND_UNLOCK(client);
return RSRV_ERROR;
}
cas_commit_msg ( client, 0 );
SEND_UNLOCK(client);
/*
* send delete confirmed message
*/
SEND_LOCK(client);

status = cas_copy_in_header ( client, pevext->msg.m_cmmd,
0u, pevext->msg.m_dataType, pevext->msg.m_count, pevext->msg.m_cid,
pevext->msg.m_available, NULL );
if ( status != ECA_NORMAL ) {
SEND_UNLOCK(client);
return RSRV_ERROR;
}
cas_commit_msg ( client, 0 );
SEND_UNLOCK(client);

freeListFree (rsrvEventFreeList, pevext);
freeListFree (rsrvEventFreeList, pevext);

return RSRV_OK;
return RSRV_OK;
}

/*
Expand Down Expand Up @@ -2385,6 +2389,75 @@ static const pProtoStubUDP udpJumpTable[] =
bad_udp_cmd_action
};

/*
* validate_camessage()
*
* Returns zero if message header is invalid.
* The function accepts 16 more commands, 16 more buffer types, and 3 more
* minor protocol versions then what is implemented currently to accomodate
* newer versions of clients to access the server without being disconnected.
*/
int validate_camessage ( caHdrLargeArray msg )
{
if (msg.m_cmmd > CA_PROTO_LAST_CMMD + 16) {
return 0;
}

switch ( msg.m_cmmd ) {
case CA_PROTO_VERSION:
case CA_PROTO_EVENT_CANCEL:
case CA_PROTO_READ:
case CA_PROTO_EVENTS_OFF:
case CA_PROTO_EVENTS_ON:
case CA_PROTO_READ_SYNC:
case CA_PROTO_CLEAR_CHANNEL:
case CA_PROTO_READ_NOTIFY:
case CA_PROTO_ECHO:
case REPEATER_REGISTER:
if ( msg.m_postsize != 0 ) return 0;
break;
case CA_PROTO_EVENT_ADD:
if ( msg.m_postsize != 16 ) return 0;
break;
case CA_PROTO_WRITE:
case CA_PROTO_WRITE_NOTIFY:
if ( msg.m_dataType > LAST_BUFFER_TYPE + 16 ) return 0;
break;
case CA_PROTO_SEARCH:
// m.count is interpreted as version minor for CA_PROTO_SEARCH
if ( msg.m_count > CA_LAST_MINOR + 3 ) return 0;
break;
case CA_PROTO_CREATE_CHAN:
case CA_PROTO_CLIENT_NAME:
case CA_PROTO_HOST_NAME:
if ( msg.m_dataType != 0 ) return 0;
break;
case CA_PROTO_ERROR:
case CA_PROTO_RSRV_IS_UP:
case CA_PROTO_NOT_FOUND:
case REPEATER_CONFIRM:
case CA_PROTO_ACCESS_RIGHTS:
case CA_PROTO_CREATE_CH_FAIL:
case CA_PROTO_SERVER_DISCONN:
// These commands are only sent from server to client, so receiving it
// here may indicate a non-CA client trying to access the server.
return 0;
break;
// We accept deprecated commands as valids for the point of view of message
// header validation. These are a weakness, as a non-CA client sending a
// message that, by chance, corresponds to the commands below will succeed
// on having the message further processed.
case CA_PROTO_SNAPSHOT:
case CA_PROTO_BUILD:
case CA_PROTO_READ_BUILD:
case CA_PROTO_SIGNAL:
return 1;
break;
}

return 1;
}

/*
* CAMESSAGE()
*/
Expand Down Expand Up @@ -2433,14 +2506,52 @@ int camessage ( struct client *client )
msg.m_cid = ntohl ( mp->m_cid );
msg.m_available = ntohl ( mp->m_available );

if ( CA_V49(client->minor_version_number) && msg.m_postsize == 0xffff ) {
/* Disconnect if a non-CA client is trying to communicate */
if ( ! validate_camessage(msg) ) {
log_header ( "CAS: Invalid channel access message rejected",
client, &msg, 0, nmsg );
status = RSRV_ERROR;
break;
}

/* Reject invalid commands */
if (msg.m_cmmd > CA_PROTO_LAST_CMMD) {
/* Log and send the error only to TCP clients. Silently ignore UDP clients */
if (client->proto == IPPROTO_TCP) {
SEND_LOCK(client);
send_err(&msg, ECA_NOSUPPORT, client, "CAS: Invalid command rejected");
SEND_UNLOCK(client);

/* By default, do not generate a log message for this type of error */
if (CASDEBUG > 0)
log_header ( "CAS: Invalid command rejected",
client, &msg, 0, nmsg );

client->recvBytesToDrain = msgsize - bytes_left;
client->recv.stk = client->recv.cnt;
}

/* Keep the connection open to avoid re-connect loops */
status = RSRV_OK;
break;
}

/*
* Calculate the message size, and update the pointer to the message payload area.
* Also, if the message is using the extended form, extract the correct payload size
* and data count from the extended message header, and update m_postsize and m_count.
*
* As the last protocol specification states, to identify extended messages,
* m_postsize must be set at 0xffff and m_count to zero.
*/
if (CA_V49(client->minor_version_number) && msg.m_postsize==0xffff && msg.m_count==0) {
ca_uint32_t *pLW = ( ca_uint32_t * ) ( mp + 1 );
if ( bytes_left < sizeof(*mp) + 2 * sizeof(*pLW) ) {
status = RSRV_OK;
break;
}
msg.m_postsize = ntohl ( pLW[0] );
msg.m_count = ntohl ( pLW[1] );
msg.m_postsize = ntohl ( pLW[0] ); /* payload size on extended form headers */
msg.m_count = ntohl ( pLW[1] ); /* Data count on extended form headers */
msgsize = msg.m_postsize + sizeof(*mp) + 2 * sizeof ( *pLW );
pBody = ( void * ) ( pLW + 2 );
}
Expand Down