Add libipm facility

This commit is contained in:
matt335672 2022-02-14 09:13:30 +00:00
parent 8f4860cb55
commit e059336dff
13 changed files with 5280 additions and 0 deletions

View File

@ -45,6 +45,7 @@ SUBDIRS = \
xup \
mc \
$(NEUTRINORDPDIR) \
libipm \
libxrdp \
$(PAINTERDIR) \
$(RFXCODECDIR) \

View File

@ -484,6 +484,7 @@ AC_CONFIG_FILES([
instfiles/pulse/Makefile
instfiles/rc.d/Makefile
keygen/Makefile
libipm/Makefile
libxrdp/Makefile
Makefile
mc/Makefile

2523
libipm/Doxyfile Normal file

File diff suppressed because it is too large Load Diff

20
libipm/Makefile.am Normal file
View File

@ -0,0 +1,20 @@
AM_CPPFLAGS = \
-I$(top_srcdir)/common
module_LTLIBRARIES = \
libipm.la
libipm_la_SOURCES = \
libipm.h \
libipm.c \
libipm_send.c \
libipm_recv.c \
libipm_facilities.h \
libipm_private.h \
scp.h \
scp.c \
scp_application_types.h
libipm_la_LIBADD = \
$(top_builddir)/common/libcommon.la

121
libipm/libipm.c Normal file
View File

@ -0,0 +1,121 @@
/**
* Copyright (C) 2022 Matt Burt, all xrdp contributors
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file libipm/libipm.c
* @brief Inter-Process Messaging building and parsing definitions
*/
#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#endif
#include "libipm.h"
#include "libipm_private.h"
#include "libipm_facilities.h"
#include "trans.h"
#include "log.h"
#include "os_calls.h"
const char *libipm_valid_type_chars = "ybnqiuxtsdhogB";
/**************************************************************************//**
* Destructor for a struct trans initialised with libipm_init_trans()
*
* @param trans Transport to destroy
*/
static void
libipm_trans_destructor(struct trans *trans)
{
struct libipm_priv *priv = (struct libipm_priv *)trans->extra_data;
if (priv != NULL)
{
if ((priv->flags & LIBIPM_E_MSG_IN_ERASE_AFTER_USE) != 0 &&
trans->in_s->data != NULL)
{
g_memset(trans->in_s->data, '\0',
trans->in_s->end - trans->in_s->data);
}
g_free(priv);
trans->extra_data = NULL;
trans->extra_destructor = NULL;
}
}
/*****************************************************************************/
enum libipm_status
libipm_init_trans(struct trans *trans,
enum libipm_facility facility,
const char *(*msgno_to_str)(unsigned short msgno))
{
struct libipm_priv *priv;
enum libipm_status rv;
if (trans->extra_data != NULL || trans->extra_destructor != NULL)
{
LOG(LOG_LEVEL_ERROR, "%s() called with sub-classed transport",
__func__);
rv = E_LI_PROGRAM_ERROR;
}
else if ((priv = g_new0(struct libipm_priv, 1)) == 0)
{
LOG(LOG_LEVEL_ERROR, "%s() out of memory", __func__);
rv = E_LI_NO_MEMORY;
}
else
{
priv->facility = facility;
priv->msgno_to_str = msgno_to_str;
trans->extra_data = priv;
trans->extra_destructor = libipm_trans_destructor;
libipm_msg_in_reset(trans);
rv = E_LI_SUCCESS;
}
return rv;
}
/*****************************************************************************/
void
libipm_set_flags(struct trans *trans, unsigned int flags)
{
struct libipm_priv *priv = (struct libipm_priv *)trans->extra_data;
if (priv != NULL)
{
priv->flags |= flags;
}
}
/*****************************************************************************/
void
libipm_clear_flags(struct trans *trans, unsigned int flags)
{
struct libipm_priv *priv = (struct libipm_priv *)trans->extra_data;
if (priv != NULL)
{
priv->flags &= ~flags;
}
}

329
libipm/libipm.h Normal file
View File

@ -0,0 +1,329 @@
/**
* Copyright (C) 2022 Matt Burt, all xrdp contributors
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file libipm/libipm.h
* @brief Inter-Process Messaging building and parsing - public declarations
*
* The functions in this file are inspired by the D-Bus type system
* (see https://dbus.freedesktop.org/doc/dbus-specification.html),
* and the sd-bus library API.
*/
#if !defined(LIBIPM_H)
#define LIBIPM_H
#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#endif
#include "arch.h"
#include "libipm_facilities.h"
struct trans;
/**
* Fixed-size block descriptor for sending blocks of data using libipm
*
* Use with the type code 'B'
*
* See the descriptions of libipm_msg_out_append() and libipm_msg_in_parse()
* for instructions on how to use this type.
*/
struct libipm_fsb
{
void *data;
unsigned int datalen;
};
/**
* Status code for library calls
*
* These are mainly intended so that the test suite can check the expected
* codepath has been followed.
*
* Wrappers around libipm may assume success is 0, and anything else
* is an error.
*/
enum libipm_status
{
E_LI_SUCCESS = 0,
E_LI_PROGRAM_ERROR, /***< Programming error */
E_LI_NO_MEMORY, /***< Memory allocaton failure */
E_LI_UNSUPPORTED_TYPE, /***< Specified type code is unsupported */
E_LI_UNIMPLEMENTED_TYPE, /***< Specified type code is unimplemented */
E_LI_UNEXPECTED_TYPE, /***< Encountered unexpected type on input */
E_LI_BUFFER_OVERFLOW, /***< End of buffer reached unexpectedly */
E_LI_BAD_VALUE, /***< Specified (or incoming) value is out of range */
E_LI_BAD_HEADER, /***< Bad incoming message header */
E_LI_TRANSPORT_ERROR /***< Error detected at the transport level */
};
/* Flags values for libipm_[set/clear]_flags() */
/**
* Input buffer contains sensitive information and must be
* erased by libipm_msg_in_reset(). The flag is one-shot, and
* is cleared after use.
*
* There is no corresponding flag for the output buffer -
* use libipm_msg_out_erase() explicitly
*/
#define LIBIPM_E_MSG_IN_ERASE_AFTER_USE (1u<<0)
/**
* Prepare a standard 'struct trans' for use with libipm
*
* @param trans libipm transport
* @param facility Facility number from libipm_facilities.h
* @param msgno_to_str Function to turn a message number into a string
* @return != 0 for error.
*
* The msgno_to_str() function is used for error logging. If the routine
* is not specified, or the routine returns NULL, a numeric code is
* logged instead.
*/
enum libipm_status
libipm_init_trans(struct trans *trans,
enum libipm_facility facility,
const char *(*msgno_to_str)(unsigned short msgno));
/**
* Set the flag(s) specified for the transport
* @param trans libipm transport
* @param flags flags to set. Multiple flags should be or'd together.
*/
void
libipm_set_flags(struct trans *trans, unsigned int flags);
/**
* Clear the flag(s) specified for the transport
* @param trans libipm transport
* @param flags flags to clear. Multiple flags should be or'd together.
*/
void
libipm_clear_flags(struct trans *trans, unsigned int flags);
/**
* Initialise an output message
*
* @param trans libipm transport
* @param msgno message number
* @param format a description of any arguments to add to the buffer, or
* NULL if no arguments are to be added at this time.
*
* See libipm_msg_out_append() for details of the format string. The format
* string is followed immediately by the arguments it describes */
enum libipm_status
libipm_msg_out_init(struct trans *trans, unsigned short msgno,
const char *format, ...);
/**
* Append more arguments to an output message
*
* @param trans libipm transport
* @param format a description of any arguments to add to the buffer.
* @param != 0 if an error occurs
*
* The format string is followed immediately by the arguments it
* describes. The format string may contain these characters (from the
* D-Bus type system):-
*
* Char | C Type | Description
* ---- |------ | -----------
* y |uint8_t | Unsigned 8-bit integer
* b |int | Boolean value (see below)
* n |int16_t | Signed (two's complement) 16-bit integer
* q |uint16_t | Unsigned 16-bit integer
* i |int32_t | Signed (two's complement) 32-bit integer
* u |uint32_t | Unsigned 32-bit integer
* x |int64_t | Signed (two's complement) 64-bit integer
* t |uint64_t | Unsigned 64-bit integer
* s |char * | NULL-terminated string
* d | - | (reserved)
* h | - | (reserved)
* o | - | (reserved)
* g | - | (reserved)
*
* For the 'b' type, only values 0 and 1 are allowed. Other values
* generate an error.
*
* The following additions to the D-Bus type system are also supported:-
*
* Char |C Type | Description
* -----|------ | -----------
* B |const struct libipm_fsb * | Fixed-size block pointer
*
* For the 'B' type, pass in the address of a descriptor containing the
* address and size of the object. The object is copied to the output
* stream, along with its length.
*/
enum libipm_status
libipm_msg_out_append(struct trans *trans, const char *format, ...);
/**
* Marks a message as complete and ready for transmission
*
* @param trans libipm transport
*/
void
libipm_msg_out_mark_end(struct trans *trans);
/**
* Convenience sending function
*
* Constructs a simple message and sends it immediately using
* libipm_msg_out_init() / libipm_msg_out_mark_end() and
* trans_force_write()
*
* @param trans libipm transport
* @param msgno message number
* @param format a description of any arguments to add to the buffer, or
* NULL if no arguments are to be added at this time.
*
* See libipm_msg_out_append() for details of the format string. The format
* string is followed immediately by the arguments it describes
*/
enum libipm_status
libipm_msg_out_simple_send(struct trans *trans, unsigned short msgno,
const char *format, ...);
/**
* Erase (rather than just ignore) the contents of an output buffer
*
* Use after sending a message containing data which should not be
* kept in memory (e.g. passwords)`
* @param trans libipm transport
*/
void
libipm_msg_out_erase(struct trans *trans);
/**
* Checks a transport to see if a complete libipm message is
* available for parsing
*
* @param trans libipm transport
* @param[out] available != 0 if a complete message is available
* @return != 0 for error
*/
enum libipm_status
libipm_msg_in_check_available(struct trans *trans, int *available);
/**
* Waits on a single transport for a libipm message to be available for
* parsing
*
* @param trans libipm transport
* @return != 0 for error
*
* While the call is active, data-in callbacks for the transport are
* disabled.
*
* Only use this call if you have nothing to do until a message
* arrives on the transport. If you have other transports to service, use
* libipm_msg_in_check_available()
*/
enum libipm_status
libipm_msg_in_wait_available(struct trans *trans);
/**
* Start parsing a message
*
* @param trans libipm transport
* @return message number in the buffer
*
* The results of calling this routine after a call to
* libipm_msg_in_reset() and before a successful call to
* libipm_msg_in_check_available() (or libipm_msg_wait_available())
* are undefined.
*
* Calling this function resets the message parsing pointer to the start
* of the message
*/
unsigned short
libipm_msg_in_start(struct trans *trans);
/**
* Returns a letter corresponding to the next available type in the
* input stream.
*
* @param trans libipm transport
* @return letter of the type, '\0' for end of message, or '?' for
* an unrecognised type
*/
char
libipm_msg_in_peek_type(struct trans *trans);
/**
* Reads arguments from an input message
*
* @param trans libipm transport
* @param format a description of the arguments to read from the buffer.
* @param != 0 if an error occurs
*
* The format string is followed immediately by the arguments it
* describes. The format string may contain these characters (from the
* D-Bus type system):-
*
* Char | C Type | Description
* ---- | ------ | -----------
* y | uint8_t * | Unsigned 8-bit integer
* b | int * | Boolean value (0 or 1 only)
* n | int16_t * | Signed (two's complement) 16-bit integer
* q | uint16_t * | Unsigned 16-bit integer
* i | int32_t * | Signed (two's complement) 32-bit integer
* u | uint32_t * | Unsigned 32-bit integer
* x | int64_t * | Signed (two's complement) 64-bit integer
* t | uint64_t * | Unsigned 64-bit integer
* s | char ** | NULL-terminated string
* d | - | (reserved)
* h | - | (reserved)
* o | - | (reserved)
* g | - | (reserved)
*
* The following additions to the D-Bus type system are also supported:-
*
* Char | C Type | Description
* -----| ------ | -----------
* B | const struct libipm_fsb * | Fixed-size block pointer
*
* For the 's' type, a pointer into the string in the input buffer is
* returned. This pointer will only be valid until the next call to
* libipm_msg_in_reset()
*
* For the 'B' type, pass in the address of an initialised descriptor
* containing the address and size of the object to copy the data
* to. The size in the descriptor must match the size of the object
* on-the-wire. Unlike 's', the data is copied out of the input buffer.
*
*/
enum libipm_status
libipm_msg_in_parse(struct trans *trans, const char *format, ...);
/**
* Resets a message buffer ready to receive the next message
*
* If the LIBIPM_E_MSG_IN_ERASE_AFTER_USE flag is set for the transport,
* the entire buffer is erased, and the flag is cleared
*
* @param trans libipm transport
*/
void
libipm_msg_in_reset(struct trans *trans);
#endif /* LIBIPM_H */

View File

@ -0,0 +1,34 @@
/**
* Copyright (C) 2022 Matt Burt, all xrdp contributors
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file libipm/libipm_facilities.h
* @brief Facilities numbers for facilities built on top of libipm
*/
#if !defined(LIBIPM__FACILITIES_H)
#define LIBIPM__FACILITIES_H
/**
* Facilities layered on top of libipm (16 bits)
*/
enum libipm_facility
{
LIBIPM_FAC_SCP = 1, /**< SCP - Sesman Control Protocol */
LIBIPM_FAC_TEST = 65535 /**< Used for unit testing */
};
#endif /* LIBIPM_FACILITIES_H */

67
libipm/libipm_private.h Normal file
View File

@ -0,0 +1,67 @@
/**
* Copyright (C) 2022 Matt Burt, all xrdp contributors
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file libipm/libipm_private.h
* @brief Inter-Process Messaging building and parsing - private declarations
*/
#if !defined(LIBIPM_PRIVATE_H)
#define LIBIPM_PRIVATE_H
#include "libipm_facilities.h"
enum
{
/**
* Max size of a libipm message
*/
LIBIPM_MAX_MSG_SIZE = 8192,
/**
* Version of libipm wire protocol
*/
LIBIPM_VERSION = 2,
/**
* Size of libipm header
*/
HEADER_SIZE = 12
};
/**
* Private class data stored in the trans 'extra_data' field
*/
struct libipm_priv
{
enum libipm_facility facility;
unsigned int flags;
const char *(*msgno_to_str)(unsigned short msgno);
unsigned short out_msgno;
unsigned short out_param_count;
unsigned short in_msgno;
unsigned short in_param_count;
};
/**
* Valid type-system characters
*
* Contains a list of all the types accepted by libipm_msg_out_append() and
* libipm_msg_in_parse()
*/
extern const char *libipm_valid_type_chars;
#endif /* LIBIPM__PRIVATE_H */

660
libipm/libipm_recv.c Normal file
View File

@ -0,0 +1,660 @@
/**
* Copyright (C) 2022 Matt Burt, all xrdp contributors
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file libipm/libipm.c
* @brief Inter-Process Messaging building and parsing definitions
*/
#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#endif
#include <stdio.h>
#include <stdarg.h>
#include "libipm.h"
#include "libipm_private.h"
#include "libipm_facilities.h"
#include "trans.h"
#include "log.h"
#include "os_calls.h"
#include "string_calls.h"
/**************************************************************************//**
* Checks the message header in the input stream, and gets the size
*/
static enum libipm_status
validate_msg_header(struct trans *trans, int *size)
{
struct libipm_priv *priv = (struct libipm_priv *)trans->extra_data;
enum libipm_status rv = E_LI_BAD_HEADER;
int version;
int facility;
int reserved;
in_uint16_le(trans->in_s, version);
in_uint16_le(trans->in_s, *size);
in_uint16_le(trans->in_s, facility);
in_uint16_le(trans->in_s, priv->in_msgno);
in_uint32_le(trans->in_s, reserved);
if (version != LIBIPM_VERSION)
{
LOG(LOG_LEVEL_ERROR,
"Unexpected version number %d from IPM", version);
}
else if (*size < HEADER_SIZE || *size > LIBIPM_MAX_MSG_SIZE)
{
LOG(LOG_LEVEL_ERROR,
"Invalid message length %d from IPM", *size);
}
else if (facility != priv->facility)
{
LOG(LOG_LEVEL_ERROR,
"Invalid facility %d from IPM - expected %d",
facility, priv->facility);
}
else if (reserved != 0)
{
LOG(LOG_LEVEL_ERROR,
"Invalid reserved field %d from IPM", reserved);
}
else
{
rv = E_LI_SUCCESS;
}
return rv;
}
/*****************************************************************************/
enum libipm_status
libipm_msg_in_check_available(struct trans *trans, int *available)
{
enum libipm_status rv = E_LI_SUCCESS;
*available = 0;
if (trans == NULL || trans->extra_data == NULL)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "Failed devel check");
rv = E_LI_PROGRAM_ERROR;
}
else if (trans->status != TRANS_STATUS_UP)
{
rv = E_LI_PROGRAM_ERROR; /* Caller should have checked this */
}
else
{
int len = trans->in_s->end - trans->in_s->data; /* Data read so far */
if (len == trans->header_size)
{
if (trans->extra_flags == 0)
{
/* We've read the header so far - validate it */
int size;
rv = validate_msg_header(trans, &size);
if (rv == 0)
{
/* Header is OK */
trans->extra_flags = 1;
trans->header_size = size;
*available = (size == HEADER_SIZE);
}
}
else
{
*available = 1;
}
}
}
return rv;
}
/*****************************************************************************/
enum libipm_status
libipm_msg_in_wait_available(struct trans *trans)
{
tbus wobj[1];
int ocnt = 0;
enum libipm_status rv = E_LI_SUCCESS;
if (trans == NULL || trans->extra_data == NULL ||
trans->status != TRANS_STATUS_UP)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "Failed devel check");
rv = E_LI_PROGRAM_ERROR;
}
else if (trans_get_wait_objs(trans, wobj, &ocnt) != 0)
{
LOG(LOG_LEVEL_ERROR, "Can't get wait object for libipm transport");
rv = E_LI_TRANSPORT_ERROR;
}
else
{
int gotmsg = 0;
/* Prevent trans_check_wait_objs() actioning any callcacks
* when the message is complete */
ttrans_data_in saved_trans_data_in = trans->trans_data_in;
trans->trans_data_in = NULL;
while (rv == E_LI_SUCCESS && !gotmsg)
{
if (g_obj_wait(wobj, ocnt, NULL, 0, -1) != 0)
{
LOG(LOG_LEVEL_ERROR, "Error waiting on libipm transport");
rv = E_LI_TRANSPORT_ERROR;
}
else if (trans_check_wait_objs(trans) != 0)
{
LOG(LOG_LEVEL_ERROR, "Error reading libipm transport");
rv = E_LI_TRANSPORT_ERROR;
}
else
{
/* This call logs errors already */
rv = libipm_msg_in_check_available(trans, &gotmsg);
}
}
/* Restore transport callback operation */
trans->trans_data_in = saved_trans_data_in;
}
return rv;
}
/*****************************************************************************/
unsigned short
libipm_msg_in_start(struct trans *trans)
{
struct libipm_priv *priv = (struct libipm_priv *)trans->extra_data;
trans->in_s->p = trans->in_s->data + HEADER_SIZE;
return (priv == NULL) ? 0 : priv->in_msgno;
}
/*****************************************************************************/
char
libipm_msg_in_peek_type(struct trans *trans)
{
int result;
if (s_check_rem(trans->in_s, 1))
{
char c;
in_uint8_peek(trans->in_s, c);
if (g_strchr(libipm_valid_type_chars, c) != NULL)
{
result = c; /* Only return valid characters */
}
else
{
result = '?';
}
}
else
{
result = '\0';
}
return result;
}
/**************************************************************************//**
* Log an input parsing error
*
* @param trans libipm transport
* @param format printf-like format descriptor
*/
printflike(2, 3) static void
log_parse_error(struct trans *trans, const char *format, ...)
{
struct libipm_priv *priv = (struct libipm_priv *)trans->extra_data;
const char *msgno_str = NULL;
char msgno_str_buff[32];
char buff[256];
unsigned int len;
/* Find a string for the message number */
if (priv->msgno_to_str != NULL)
{
msgno_str = priv->msgno_to_str(priv->in_msgno);
}
if (msgno_str == NULL)
{
g_snprintf(msgno_str_buff, sizeof(msgno_str_buff),
"[code #%d]", priv->in_msgno);
msgno_str = msgno_str_buff;
}
len = g_snprintf(buff, sizeof(buff),
"Error parsing ipm message for %s, parameter %d :",
msgno_str, priv->in_param_count);
if (len < sizeof(buff))
{
va_list ap;
va_start(ap, format);
vsnprintf(&buff[len], sizeof(buff) - len, format, ap);
va_end(ap);
}
LOG(LOG_LEVEL_ERROR, "%s", buff);
}
/* Common format string for overflow reporting */
static const char *
not_enough_input_msg = "Input buffer overflow for '%c'";
/**************************************************************************//**
* Extract a bool from the input stream
*
* @param c Type letter which triggered the call
* @param trans libipm transport
* @param argptr Pointer to pointer to receive the value
* @return != 0 for error
*
* The value must be a 0 or 1 on-the-wire, or an error is returned.
*/
static enum libipm_status
extract_bool_type(char c, struct trans *trans, va_list argptr)
{
enum libipm_status rv = E_LI_SUCCESS;
struct stream *s = trans->in_s;
int b;
if (!s_check_rem(s, 1))
{
log_parse_error(trans, not_enough_input_msg, c);
rv = E_LI_BUFFER_OVERFLOW;
}
else
{
in_uint8(s, b);
if (b < 0 || b > 1)
{
log_parse_error(trans, "Boolean has value other than 0/1");
rv = E_LI_BAD_VALUE;
}
else
{
int *tmp = va_arg(argptr, int *);
*tmp = b;
}
}
return rv;
}
/**************************************************************************//**
* Extract an octet from the input stream
*
* @param c Type letter which triggered the call
* @param trans libipm transport
* @param argptr argptr to pointer to receive the value
* @return != 0 for error
*/
static enum libipm_status
extract_int8_type(char c, struct trans *trans, va_list argptr)
{
enum libipm_status rv = E_LI_SUCCESS;
struct stream *s = trans->in_s;
if (!s_check_rem(s, 1))
{
log_parse_error(trans, not_enough_input_msg, c);
rv = E_LI_BUFFER_OVERFLOW;
}
else
{
unsigned char *tmp = va_arg(argptr, unsigned char *);
in_uint8(s, *tmp);
}
return rv;
}
/**************************************************************************//**
* Extract a 16-bit integer from the input stream
*
* @param c Type letter which triggered the call
* @param trans libipm transport
* @param argptr argptr to pointer to receive the value
* @return != 0 for error
*/
static enum libipm_status
extract_int16_type(char c, struct trans *trans, va_list argptr)
{
enum libipm_status rv = E_LI_SUCCESS;
struct stream *s = trans->in_s;
if (!s_check_rem(s, 2))
{
log_parse_error(trans, not_enough_input_msg, c);
rv = E_LI_BUFFER_OVERFLOW;
}
else
{
uint16_t *tmp = va_arg(argptr, uint16_t *);
/*
* C99 7.18.1.1 requires int16_t (if present) to be a two's
* complement representation, so this line is valid for both
* int16_t and uint16_t */
in_uint16_le(s, *tmp);
}
return rv;
}
/**************************************************************************//**
* Extract a 32-bit integer from the input stream
*
* @param c Type letter which triggered the call
* @param trans libipm transport
* @param argptr argptr to pointer to receive the value
* @return != 0 for error
*/
static enum libipm_status
extract_int32_type(char c, struct trans *trans, va_list argptr)
{
enum libipm_status rv = E_LI_SUCCESS;
struct stream *s = trans->in_s;
if (!s_check_rem(s, 4))
{
log_parse_error(trans, not_enough_input_msg, c);
rv = E_LI_BUFFER_OVERFLOW;
}
else
{
uint32_t *tmp = va_arg(argptr, uint32_t *);
/*
* C99 7.18.1.1 requires int32_t (if present) to be a two's
* complement representation, so this line is valid for both
* int32_t and uint32_t */
in_uint32_le(s, *tmp);
}
return rv;
}
/**************************************************************************//**
* Extract a 64-bit integer from the input stream
*
* @param c Type letter which triggered the call
* @param trans libipm transport
* @param argptr argptr to pointer to receive the value
* @return != 0 for error
*/
static enum libipm_status
extract_int64_type(char c, struct trans *trans, va_list argptr)
{
enum libipm_status rv = E_LI_SUCCESS;
struct stream *s = trans->in_s;
if (!s_check_rem(s, 8))
{
log_parse_error(trans, not_enough_input_msg, c);
rv = E_LI_BUFFER_OVERFLOW;
}
else
{
uint64_t *tmp = va_arg(argptr, uint64_t *);
/*
* C99 7.18.1.1 requires int64_t (if present) to be a two's
* complement representation, so this line is valid for both
* int64_t and uint64_t */
in_uint64_le(s, *tmp);
}
return rv;
}
/**************************************************************************//**
* Extract a char * pointer from the input stream
*
* @param c Type letter which triggered the call
* @param trans libipm transport
* @param argptr argptr to pointer to receive the value
* @return != 0 for error
*/
static enum libipm_status
extract_char_ptr_type(char c, struct trans *trans, va_list argptr)
{
enum libipm_status rv = E_LI_SUCCESS;
struct stream *s = trans->in_s;
/* Look for a string terminator in the rest of the input stream */
char *termptr = g_strnchr(s->p, '\0', s->end - s->p);
if (termptr == NULL)
{
log_parse_error(trans, "Unterminated string value");
rv = E_LI_BAD_VALUE;
}
else
{
char **tmp = va_arg(argptr, char **);
*tmp = s->p;
s->p = termptr + 1;
}
return rv;
}
/**************************************************************************//**
* Extract a fixed size block from the input stream
*
* @param c Type letter which triggered the call
* @param trans libipm transport
* @param argptr argptr to pointer to block descriptor
* @return != 0 for error
*/
static enum libipm_status
extract_fsb_type(char c, struct trans *trans, va_list argptr)
{
enum libipm_status rv = E_LI_SUCCESS;
struct stream *s = trans->in_s;
const struct libipm_fsb *fsb = va_arg(argptr, const struct libipm_fsb *);
if (fsb == NULL || fsb->data == NULL)
{
log_parse_error(trans, "Malformed descriptor for '%c'", c);
rv = E_LI_PROGRAM_ERROR;
}
else if (!s_check_rem(s, 2))
{
log_parse_error(trans, not_enough_input_msg, c);
rv = E_LI_BUFFER_OVERFLOW;
}
else
{
unsigned int len;
in_uint16_le(s, len);
if (len != fsb->datalen)
{
log_parse_error(trans, "Type '%c'. Expected %u bytes, but got %u",
c, fsb->datalen, len);
rv = E_LI_BAD_VALUE;
}
else if (!s_check_rem(s, len))
{
log_parse_error(trans, not_enough_input_msg, c);
rv = E_LI_BUFFER_OVERFLOW;
}
else
{
in_uint8a(s, fsb->data, len);
rv = E_LI_SUCCESS;
}
}
return rv;
}
/**************************************************************************//**
* Local function to pull data from the input stream
*
* @param trans libipm transport
* @param format type-system compatible string
* @param argptr Pointers to variables to receive extracted data
* @pre - trans->priv has been checked to be non-NULL
* @return != 0 for error
*/
static enum libipm_status
libipm_msg_in_parsev(struct trans *trans, const char *format, va_list argptr)
{
enum libipm_status rv = E_LI_SUCCESS;
struct stream *s = trans->in_s;
struct libipm_priv *priv = (struct libipm_priv *)trans->extra_data;
const char *cp;
if (format != NULL)
{
for (cp = format; rv == 0 && *cp != '\0' ; ++cp)
{
char c = *cp;
char actual_c;
++priv->in_param_count; /* Count the parameter */
/* Check the type of the input is supported */
if (g_strchr(libipm_valid_type_chars, c) == NULL)
{
log_parse_error(trans,
"Type code '%c' is not supported", c);
rv = E_LI_UNSUPPORTED_TYPE;
break;
}
/* Check the type of the input matches the stream */
if (!s_check_rem(s, 1))
{
log_parse_error(trans, not_enough_input_msg, c);
rv = E_LI_BUFFER_OVERFLOW;
break;
}
in_uint8(s, actual_c);
if (c != actual_c)
{
log_parse_error(trans, "Expected '%c', got '%c'", c, actual_c);
rv = E_LI_UNEXPECTED_TYPE;
break;
}
switch (c)
{
case 'y':
rv = extract_int8_type(c, trans, argptr);
break;
case 'b':
rv = extract_bool_type(c, trans, argptr);
break;
case 'n':
case 'q':
rv = extract_int16_type(c, trans, argptr);
break;
case 'i':
case 'u':
rv = extract_int32_type(c, trans, argptr);
break;
case 'x':
case 't':
rv = extract_int64_type(c, trans, argptr);
break;
case 's':
rv = extract_char_ptr_type(c, trans, argptr);
break;
case 'B':
rv = extract_fsb_type(c, trans, argptr);
break;
default:
log_parse_error(trans,
"Reserved type code '%c' "
"is unimplemented", c);
rv = E_LI_UNIMPLEMENTED_TYPE;
break;
}
}
}
return rv;
}
/*****************************************************************************/
enum libipm_status
libipm_msg_in_parse(struct trans *trans, const char *format, ...)
{
enum libipm_status rv;
struct libipm_priv *priv = (struct libipm_priv *)trans->extra_data;
if (priv == NULL)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "uninitialised transport");
rv = E_LI_PROGRAM_ERROR;
}
else
{
va_list argptr;
va_start(argptr, format);
rv = libipm_msg_in_parsev(trans, format, argptr);
va_end(argptr);
}
return rv;
}
/*****************************************************************************/
void
libipm_msg_in_reset(struct trans *trans)
{
struct libipm_priv *priv = (struct libipm_priv *)trans->extra_data;
if (priv == NULL)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "uninitialised transport");
}
else
{
if ((priv->flags & LIBIPM_E_MSG_IN_ERASE_AFTER_USE) != 0)
{
struct stream *s = trans->in_s;
g_memset(s->data, '\0', s->end - s->data);
priv->flags &= ~LIBIPM_E_MSG_IN_ERASE_AFTER_USE;
}
priv->in_msgno = 0;
priv->in_param_count = 0;
}
trans->extra_flags = 0;
trans->header_size = HEADER_SIZE;
trans->no_stream_init_on_data_in = 1;
init_stream(trans->in_s, LIBIPM_MAX_MSG_SIZE);
}

570
libipm/libipm_send.c Normal file
View File

@ -0,0 +1,570 @@
/**
* Copyright (C) 2022 Matt Burt, all xrdp contributors
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file libipm/libipm.c
* @brief Inter-Process Messaging building and parsing definitions
*/
#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#endif
#include <stdio.h>
#include <stdarg.h>
#include "libipm.h"
#include "libipm_private.h"
#include "libipm_facilities.h"
#include "trans.h"
#include "log.h"
#include "os_calls.h"
#include "string_calls.h"
/**************************************************************************//**
* Log an output appending error
*
* @param trans libipm transport
* @param format printf-like format descriptor
*/
printflike(2, 3) static void
log_append_error(struct trans *trans, const char *format, ...)
{
struct libipm_priv *priv = (struct libipm_priv *)trans->extra_data;
const char *msgno_str = NULL;
char msgno_str_buff[32];
char buff[256];
unsigned int len;
/* Find a string for the message number */
if (priv->msgno_to_str != NULL)
{
msgno_str = priv->msgno_to_str(priv->out_msgno);
}
if (msgno_str == NULL)
{
g_snprintf(msgno_str_buff, sizeof(msgno_str_buff),
"[code #%d]", priv->out_msgno);
msgno_str = msgno_str_buff;
}
len = g_snprintf(buff, sizeof(buff),
"Error creating ipm message for %s, parameter %d :",
msgno_str, priv->out_param_count);
if (len < sizeof(buff))
{
va_list ap;
va_start(ap, format);
vsnprintf(&buff[len], sizeof(buff) - len, format, ap);
va_end(ap);
}
LOG(LOG_LEVEL_ERROR, "%s", buff);
}
/* Common format string for overflow reporting */
static const char *
not_enough_output_msg = "Not enough space in output buffer for '%c'";
/* Common format string for bad value reporting */
static const char *
bad_value_msg = "Type '%c' has unsupported value '%d'";
/**************************************************************************//**
* Add a bool to the output stream
*
* @param c Type letter which triggered the call
* @param trans libipm transport
* @param argptr Pointer to value in argument stack (promoted to int)
* @return != 0 for error
*
* The boolean value must be a 0 or a 1.
*/
static enum libipm_status
append_bool_type(char c, va_list argptr, struct trans *trans)
{
enum libipm_status rv = E_LI_SUCCESS;
struct stream *s = trans->out_s;
if (!s_check_rem_out(s, 1 + 1))
{
log_append_error(trans, not_enough_output_msg, c);
rv = E_LI_BUFFER_OVERFLOW;
}
else
{
int tmp = va_arg(argptr, int);
if (tmp < 0 || tmp > 1)
{
log_append_error(trans, bad_value_msg, c, tmp);
rv = E_LI_BAD_VALUE;
}
else
{
out_uint8(s, c);
out_uint8(s, tmp);
}
}
return rv;
}
/**************************************************************************//**
* Add an octet to the output stream
*
* @param c Type letter which triggered the call
* @param trans libipm transport
* @param argptr Pointer to value in argument stack (promoted to int)
* @return != 0 for error
*/
static enum libipm_status
append_int8_type(char c, va_list argptr, struct trans *trans)
{
enum libipm_status rv = E_LI_SUCCESS;
struct stream *s = trans->out_s;
if (!s_check_rem_out(s, 1 + 1))
{
log_append_error(trans, not_enough_output_msg, c);
rv = E_LI_BUFFER_OVERFLOW;
}
else
{
int tmp = va_arg(argptr, int);
if (tmp < 0 || tmp > 255)
{
log_append_error(trans, bad_value_msg, c, tmp);
rv = E_LI_BAD_VALUE;
}
else
{
out_uint8(s, c);
out_uint8(s, tmp);
}
}
return rv;
}
/**************************************************************************//**
* Add an 16-bit integer to the output stream
*
* @param c Type letter which triggered the call
* @param trans libipm transport
* @param argptr Pointer to value in argument stack (promoted to int)
* @return != 0 for error
*/
static enum libipm_status
append_int16_type(char c, va_list argptr, struct trans *trans)
{
enum libipm_status rv = E_LI_SUCCESS;
struct stream *s = trans->out_s;
if (!s_check_rem_out(s, 1 + 2))
{
log_append_error(trans, not_enough_output_msg, c);
rv = E_LI_BUFFER_OVERFLOW;
}
else
{
int tmp = va_arg(argptr, int);
if ((c == 'n' && (tmp < -0x8000 || tmp > 0x7fff)) ||
(c == 'q' && (tmp < 0 || tmp > 0xffff)))
{
log_append_error(trans, bad_value_msg, c, tmp);
rv = E_LI_BAD_VALUE;
}
else
{
out_uint8(s, c);
out_uint16_le(s, tmp);
}
}
return rv;
}
/**************************************************************************//**
* Add a 32-bit integer to the output stream
*
* @param c Type letter which triggered the call
* @param trans libipm transport
* @param argptr Pointer to value in argument stack
* @return != 0 for error
*/
static enum libipm_status
append_int32_type(char c, va_list argptr, struct trans *trans)
{
enum libipm_status rv = E_LI_SUCCESS;
struct stream *s = trans->out_s;
if (!s_check_rem_out(s, 1 + 4))
{
log_append_error(trans, not_enough_output_msg, c);
rv = E_LI_BUFFER_OVERFLOW;
}
else
{
/* If int is bigger than 4 bytes, the argument will be
* promoted to 'int' rather than 'int32_t'/'uint32_t',
* and we will need to check the specified value is in range.
*/
#if SIZEOF_INT > 4
int tmp = va_arg(argptr, int);
if ((c == 'i' && (tmp < -0x80000000 || tmp > 0x7fffffff)) ||
(c == 'u' && (tmp < 0 || tmp > 0xffffffff)))
{
log_append_error(trans, bad_value_msg, c, tmp);
rv = E_LI_BAD_VALUE;
}
else
{
out_uint8(s, c);
out_uint32_le(s, tmp);
}
#else
uint32_t tmp = va_arg(argptr, uint32_t);
out_uint8(s, c);
out_uint32_le(s, tmp);
#endif
}
return rv;
}
/**************************************************************************//**
* Add a 64-bit integer to the output stream
*
* @param c Type letter which triggered the call
* @param trans libipm transport
* @param argptr Pointer to value in argument stack
* @return != 0 for error
*/
static enum libipm_status
append_int64_type(char c, va_list argptr, struct trans *trans)
{
enum libipm_status rv = E_LI_SUCCESS;
struct stream *s = trans->out_s;
if (!s_check_rem_out(s, 1 + 8))
{
log_append_error(trans, not_enough_output_msg, c);
rv = E_LI_BUFFER_OVERFLOW;
}
else
{
uint64_t tmp = va_arg(argptr, uint64_t);
out_uint8(s, c);
out_uint64_le(s, tmp);
}
return rv;
}
/**************************************************************************//**
* Add a terminated string to the output stream
*
* @param c Type letter which triggered the call
* @param trans libipm transport
* @param argptr Pointer to value in argument stack (promoted to int)
* @return != 0 for error
*
* NULL pointers are not allowed for the string.
*/
static enum libipm_status
append_char_ptr_type(char c, va_list argptr, struct trans *trans)
{
enum libipm_status rv = E_LI_SUCCESS;
struct stream *s = trans->out_s;
const char *str = va_arg(argptr, const char *);
if (str == NULL)
{
log_append_error(trans, "String cannot be NULL");
rv = E_LI_PROGRAM_ERROR;
}
else
{
unsigned int len = g_strlen(str);
if ((len > (LIBIPM_MAX_MSG_SIZE - HEADER_SIZE - 1)) ||
(!s_check_rem_out(s, 1 + 1 + len)))
{
log_append_error(trans,
"Not enough space in output buffer for "
"'%c'[len=%u]", c, len);
rv = E_LI_BUFFER_OVERFLOW;
}
else
{
out_uint8(s, c);
out_uint8p(s, str, len + 1);
}
}
return rv;
}
/**************************************************************************//**
* Append a fixed size block to the output stream
*
* @param c Type letter which triggered the call
* @param trans libipm transport
* @param argptr argptr to pointer to block descriptor
* @return != 0 for error
*/
static enum libipm_status
append_fsb_type(char c, va_list argptr, struct trans *trans)
{
enum libipm_status rv = E_LI_SUCCESS;
struct stream *s = trans->out_s;
const struct libipm_fsb *fsb = va_arg(argptr, const struct libipm_fsb *);
if (fsb == NULL || fsb->data == NULL)
{
log_append_error(trans, "Malformed descriptor for '%c'", c);
rv = E_LI_PROGRAM_ERROR;
}
else
{
unsigned int len = fsb->datalen;
if ((len > (LIBIPM_MAX_MSG_SIZE - HEADER_SIZE - 2)) ||
(!s_check_rem_out(s, 1 + 2 + len)))
{
log_append_error(trans, "Not enough space in output buffer"
" for '%c'[len=%u]", c, len);
rv = E_LI_BUFFER_OVERFLOW;
}
else
{
out_uint8(s, c);
out_uint16_le(s, len);
out_uint8a(s, fsb->data, len);
}
}
return rv;
}
/**************************************************************************//**
* Local function to append data to the output stream
*
* @param trans libipm transport
* @param format type-system compatible string
* @param argptr Variables containing data to append
* @pre - trans->priv has been checked to be non-NULL
* @return != 0 for error
*/
static enum libipm_status
libipm_msg_out_appendv(struct trans *trans, const char *format, va_list argptr)
{
enum libipm_status rv = E_LI_SUCCESS;
struct libipm_priv *priv = (struct libipm_priv *)trans->extra_data;
const char *cp;
if (format != NULL)
{
for (cp = format; rv == 0 && *cp != '\0' ; ++cp)
{
char c = *cp;
++priv->out_param_count; /* Count the parameter */
if (g_strchr(libipm_valid_type_chars, c) == NULL)
{
log_append_error(trans,
"Type code '%c' is not supported", c);
rv = E_LI_UNSUPPORTED_TYPE;
break;
}
switch (c)
{
case 'y':
rv = append_int8_type(c, argptr, trans);
break;
case 'b':
rv = append_bool_type(c, argptr, trans);
break;
case 'n':
case 'q':
rv = append_int16_type(c, argptr, trans);
break;
case 'i':
case 'u':
rv = append_int32_type(c, argptr, trans);
break;
case 'x':
case 't':
rv = append_int64_type(c, argptr, trans);
break;
case 's':
rv = append_char_ptr_type(c, argptr, trans);
break;
case 'B':
rv = append_fsb_type(c, argptr, trans);
break;
default:
log_append_error(trans,
"Reserved type code '%c' "
"is unimplemented", c);
rv = E_LI_UNIMPLEMENTED_TYPE;
break;
}
}
}
return rv;
}
/**************************************************************************//**
* Prepare the tranport to build an output message
* @param trans libipm trans
* @param msgno Number of message
* @return != 0 for error
*/
static void
init_output_buffer(struct trans *trans, unsigned short msgno)
{
struct stream *s = trans->out_s;
struct libipm_priv *priv = (struct libipm_priv *)trans->extra_data;
init_stream(s, LIBIPM_MAX_MSG_SIZE);
/* Leave space for header */
s_push_layer(s, iso_hdr, HEADER_SIZE);
priv->out_msgno = msgno;
priv->out_param_count = 0;
}
/*****************************************************************************/
enum libipm_status
libipm_msg_out_init(struct trans *trans, unsigned short msgno,
const char *format, ...)
{
enum libipm_status rv;
struct libipm_priv *priv = (struct libipm_priv *)trans->extra_data;
if (priv == NULL)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "uninitialised transport");
rv = E_LI_PROGRAM_ERROR;
}
else
{
va_list argptr;
init_output_buffer(trans, msgno);
va_start(argptr, format);
rv = libipm_msg_out_appendv(trans, format, argptr);
va_end(argptr);
}
return rv;
}
/*****************************************************************************/
enum libipm_status
libipm_msg_out_append(struct trans *trans, const char *format, ...)
{
enum libipm_status rv;
struct libipm_priv *priv = (struct libipm_priv *)trans->extra_data;
if (priv == NULL)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "uninitialised transport");
rv = E_LI_PROGRAM_ERROR;
}
else
{
va_list argptr;
va_start(argptr, format);
rv = libipm_msg_out_appendv(trans, format, argptr);
va_end(argptr);
}
return rv;
}
/*****************************************************************************/
void
libipm_msg_out_mark_end(struct trans *trans)
{
struct libipm_priv *priv = (struct libipm_priv *)trans->extra_data;
if (priv == NULL)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "uninitialised transport");
}
else
{
struct stream *s = trans->out_s;
s_mark_end(s);
s_pop_layer(s, iso_hdr);
/* Write the message header */
out_uint16_le(s, LIBIPM_VERSION);
out_uint16_le(s, s->end - s->data);
out_uint16_le(s, priv->facility);
out_uint16_le(s, priv->out_msgno);
out_uint32_le(s, 0); /* Reserved */
/* Move the output pointer back to the end so another
* append works, and so libipm_msg_out_erase() knows
* exactly what to do */
s->p = s->end;
}
}
/*****************************************************************************/
enum libipm_status
libipm_msg_out_simple_send(struct trans *trans, unsigned short msgno,
const char *format, ...)
{
enum libipm_status rv;
struct libipm_priv *priv = (struct libipm_priv *)trans->extra_data;
if (priv == NULL)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "uninitialised transport");
rv = E_LI_PROGRAM_ERROR;
}
else
{
va_list argptr;
va_start(argptr, format);
init_output_buffer(trans, msgno);
rv = libipm_msg_out_appendv(trans, format, argptr);
if (rv == E_LI_SUCCESS)
{
libipm_msg_out_mark_end(trans);
if (trans_force_write(trans) != 0)
{
rv = E_LI_TRANSPORT_ERROR;
}
}
va_end(argptr);
}
return rv;
}
/*****************************************************************************/
void
libipm_msg_out_erase(struct trans *trans)
{
struct stream *s = trans->out_s;
if (s->size > 0 && s->data != 0)
{
g_memset(s->data, '\0', s->p - s->data);
}
}

505
libipm/scp.c Normal file
View File

@ -0,0 +1,505 @@
/**
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) Jay Sorg 2004-2022, all xrdp contributors
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
*
* @file libipm/scp.c
* @brief scp definitions
* @author Simone Fedele/ Matt Burt
*/
#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#endif
#include "scp.h"
#include "libipm.h"
#include "guid.h"
#include "trans.h"
#include "os_calls.h"
#include "string_calls.h"
/*****************************************************************************/
static const char *
msgno_to_str(unsigned short n)
{
return
(n == E_SCP_GATEWAY_REQUEST) ? "SCP_GATEWAY_REQUEST" :
(n == E_SCP_GATEWAY_RESPONSE) ? "SCP_GATEWAY_RESPONSE" :
(n == E_SCP_CREATE_SESSION_REQUEST) ? "SCP_CREATE_SESSION_REQUEST" :
(n == E_SCP_CREATE_SESSION_RESPONSE) ? "SCP_CREATE_SESSION_RESPONSE" :
(n == E_SCP_LIST_SESSIONS_REQUEST) ? "SCP_LIST_SESSIONS_REQUEST" :
(n == E_SCP_LIST_SESSIONS_RESPONSE) ? "SCP_LIST_SESSIONS_RESPONSE" :
NULL;
}
/*****************************************************************************/
const char *
scp_msgno_to_str(enum scp_msg_code n, char *buff, unsigned int buff_size)
{
const char *str = msgno_to_str((unsigned short)n);
if (str == NULL)
{
g_snprintf(buff, buff_size, "[code #%d]", (int)n);
}
else
{
g_snprintf(buff, buff_size, "%s", str);
}
return buff;
}
/*****************************************************************************/
struct trans *
scp_connect(const char *host, const char *port,
int (*term_func)(void))
{
struct trans *t;
if ((t = trans_create(TRANS_MODE_TCP, 128, 128)) != NULL)
{
int index;
if (host == NULL)
{
host = "localhost";
}
if (port == NULL)
{
port = "3350";
}
t->is_term = term_func;
/* try to connect up to 4 times
*
* term_func can be NULL, so check before calling it */
index = 4;
while (trans_connect(t, host, port, 3000) != 0 &&
!(term_func && term_func()) &&
--index > 0)
{
g_sleep(1000);
LOG_DEVEL(LOG_LEVEL_DEBUG, "Connect failed. Trying again...");
}
if (t->status != TRANS_STATUS_UP)
{
trans_delete(t);
t = NULL;
}
else if (libipm_init_trans(t, LIBIPM_FAC_SCP, msgno_to_str) !=
E_LI_SUCCESS)
{
trans_delete(t);
t = NULL;
}
}
return t;
}
/*****************************************************************************/
int
scp_init_trans(struct trans *trans)
{
return libipm_init_trans(trans, LIBIPM_FAC_SCP, msgno_to_str);
}
/*****************************************************************************/
int
scp_msg_in_check_available(struct trans *trans, int *available)
{
return libipm_msg_in_check_available(trans, available);
}
/*****************************************************************************/
int
scp_msg_in_wait_available(struct trans *trans)
{
return libipm_msg_in_wait_available(trans);
}
/*****************************************************************************/
void
scp_msg_in_reset(struct trans *trans)
{
libipm_msg_in_reset(trans);
}
/*****************************************************************************/
enum scp_msg_code
scp_msg_in_start(struct trans *trans)
{
return (enum scp_msg_code)libipm_msg_in_start(trans);
}
/*****************************************************************************/
int
scp_send_gateway_request(struct trans *trans,
const char *username,
const char *password)
{
int rv;
rv = libipm_msg_out_simple_send(
trans,
(int)E_SCP_GATEWAY_REQUEST,
"ss",
username,
password);
/* Wipe the output buffer to remove the password */
libipm_msg_out_erase(trans);
return rv;
}
/*****************************************************************************/
int
scp_get_gateway_request(struct trans *trans,
const char **username,
const char **password)
{
/* Make sure the buffer is cleared after processing this message */
libipm_set_flags(trans, LIBIPM_E_MSG_IN_ERASE_AFTER_USE);
return libipm_msg_in_parse(trans, "ss", username, password);
}
/*****************************************************************************/
int
scp_send_gateway_response(struct trans *trans,
int auth_result)
{
return libipm_msg_out_simple_send(
trans,
(int)E_SCP_GATEWAY_RESPONSE,
"i",
auth_result);
}
/*****************************************************************************/
int
scp_get_gateway_response(struct trans *trans,
int *auth_result)
{
int32_t i_auth_result = 0;
int rv = libipm_msg_in_parse(trans, "i", &i_auth_result);
if (rv == 0)
{
*auth_result = i_auth_result;
}
return rv;
}
/*****************************************************************************/
int
scp_send_create_session_request(struct trans *trans,
const char *username,
const char *password,
enum scp_session_type type,
unsigned short width,
unsigned short height,
unsigned char bpp,
const char *shell,
const char *directory,
const char *connection_description)
{
int rv = libipm_msg_out_simple_send(
trans,
(int)E_SCP_CREATE_SESSION_REQUEST,
"ssyqqysss",
username,
password,
type,
width,
height,
bpp,
shell,
directory,
connection_description);
/* Wipe the output buffer to remove the password */
libipm_msg_out_erase(trans);
return rv;
}
/*****************************************************************************/
int
scp_get_create_session_request(struct trans *trans,
const char **username,
const char **password,
enum scp_session_type *type,
unsigned short *width,
unsigned short *height,
unsigned char *bpp,
const char **shell,
const char **directory,
const char **connection_description)
{
/* Intermediate values */
uint8_t i_type;
uint16_t i_width;
uint16_t i_height;
uint8_t i_bpp;
/* Make sure the buffer is cleared after processing this message */
libipm_set_flags(trans, LIBIPM_E_MSG_IN_ERASE_AFTER_USE);
int rv = libipm_msg_in_parse(
trans,
"ssyqqysss",
username,
password,
&i_type,
&i_width,
&i_height,
&i_bpp,
shell,
directory,
connection_description);
if (rv == 0)
{
*type = (enum scp_session_type)i_type;
*width = i_width;
*height = i_height;
/* bpp is fixed for Xorg session types */
*bpp = (*type == SCP_SESSION_TYPE_XORG) ? 24 : i_bpp;
}
return rv;
}
/*****************************************************************************/
int
scp_send_create_session_response(struct trans *trans,
int auth_result,
int display,
const struct guid *guid)
{
struct libipm_fsb guid_descriptor = { (void *)guid, sizeof(*guid) };
return libipm_msg_out_simple_send(
trans,
(int)E_SCP_CREATE_SESSION_RESPONSE,
"iiB",
auth_result,
display,
&guid_descriptor);
}
/*****************************************************************************/
int
scp_get_create_session_response(struct trans *trans,
int *auth_result,
int *display,
struct guid *guid)
{
/* Intermediate values */
int32_t i_auth_result;
int32_t i_display;
const struct libipm_fsb guid_descriptor = { (void *)guid, sizeof(*guid) };
int rv = libipm_msg_in_parse(
trans,
"iiB",
&i_auth_result,
&i_display,
&guid_descriptor);
if (rv == 0)
{
*auth_result = i_auth_result;
*display = i_display;
}
return rv;
}
/*****************************************************************************/
int
scp_send_list_sessions_request(struct trans *trans,
const char *username,
const char *password)
{
int rv;
rv = libipm_msg_out_simple_send(
trans,
(int)E_SCP_LIST_SESSIONS_REQUEST,
"ss",
username,
password);
/* Wipe the output buffer to remove the password */
libipm_msg_out_erase(trans);
return rv;
}
/*****************************************************************************/
int
scp_get_list_sessions_request(struct trans *trans,
const char **username,
const char **password)
{
/* Make sure the buffer is cleared after processing this message */
libipm_set_flags(trans, LIBIPM_E_MSG_IN_ERASE_AFTER_USE);
return libipm_msg_in_parse(trans, "ss", username, password);
}
/*****************************************************************************/
int
scp_send_list_sessions_response(
struct trans *trans,
enum scp_list_sessions_status status,
const struct scp_session_info *info)
{
int rv;
if (status != E_SCP_LS_SESSION_INFO)
{
rv = libipm_msg_out_simple_send(
trans,
(int)E_SCP_LIST_SESSIONS_RESPONSE,
"i", status);
}
else
{
rv = libipm_msg_out_simple_send(
trans,
(int)E_SCP_LIST_SESSIONS_RESPONSE,
"iiuyqqyxss",
status,
info->sid,
info->display,
info->type,
info->width,
info->height,
info->bpp,
info->start_time,
info->username,
info->connection_description);
}
return rv;
}
/*****************************************************************************/
int
scp_get_list_sessions_response(
struct trans *trans,
enum scp_list_sessions_status *status,
struct scp_session_info **info)
{
int32_t i_status;
int rv;
if (info == NULL)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "Bad pointer in %s", __func__);
rv = 1;
}
else if ((rv = libipm_msg_in_parse(trans, "i", &i_status)) == 0)
{
*status = (enum scp_list_sessions_status)i_status;
struct scp_session_info *p = NULL;
if (*status == E_SCP_LS_SESSION_INFO)
{
int32_t i_sid;
uint32_t i_display;
uint8_t i_type;
uint16_t i_width;
uint16_t i_height;
uint8_t i_bpp;
int64_t i_start_time;
char *i_username;
char *i_connection_description;
rv = libipm_msg_in_parse(
trans,
"iuyqqyxss",
&i_sid,
&i_display,
&i_type,
&i_width,
&i_height,
&i_bpp,
&i_start_time,
&i_username,
&i_connection_description);
if (rv == 0)
{
/* Allocate a block of memory large enough for the
* structure result, and the strings it contains */
unsigned int len = sizeof(struct scp_session_info) +
g_strlen(i_username) + 1 +
g_strlen(i_connection_description) + 1;
if ((p = (struct scp_session_info *)g_malloc(len, 1)) == NULL)
{
*status = E_SCP_LS_NO_MEMORY;
}
else
{
/* Set up the string pointers in the block to point
* into the memory allocated after the block */
p->username = (char *)p + sizeof(struct scp_session_info);
p->connection_description =
p->username + g_strlen(i_username) + 1;
/* Copy the data over */
p->sid = i_sid;
p->display = i_display;
p->type = (enum scp_session_type)i_type;
p->width = i_width;
p->height = i_height;
p->bpp = i_bpp;
p->start_time = i_start_time;
g_strcpy(p->username, i_username);
g_strcpy(p->connection_description,
i_connection_description);
}
}
}
*info = p;
}
return rv;
}

384
libipm/scp.h Normal file
View File

@ -0,0 +1,384 @@
/**
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) Jay Sorg 2004-2022, all xrdp contributors
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
*
* @file libipm/scp.h
* @brief scp declarations
* @author Simone Fedele/ Matt Burt
*
* Functions in this file use the following naming conventions:-
*
* E_SCP_<msg>_REQUEST is sent by scp_send_<msg>_request()
* E_SCP_<msg>_REQUEST is parsed by scp_get_<msg>_request()
* E_SCP_<msg>_RESPONSE is sent by scp_send_<msg>_response()
* E_SCP_<msg>_RESPONSE is parsed by scp_get_<msg>_response()
*/
#ifndef SCP_H
#define SCP_H
#include "arch.h"
struct guid;
struct trans;
#include "scp_application_types.h"
/* Message codes */
enum scp_msg_code
{
E_SCP_GATEWAY_REQUEST = 1,
E_SCP_GATEWAY_RESPONSE,
E_SCP_CREATE_SESSION_REQUEST,
E_SCP_CREATE_SESSION_RESPONSE,
E_SCP_LIST_SESSIONS_REQUEST,
E_SCP_LIST_SESSIONS_RESPONSE
};
/* Common facilities */
/**
* Convert a message code to a string for output
* @param n Message code
* @param buff to contain string
* @param buff_size length of buff
* @return buff is returned for convenience.
*/
const char *
scp_msgno_to_str(enum scp_msg_code n, char *buff, unsigned int buff_size);
/* Connection management facilities */
/**
* Connect to an SCP server
*
* @param host Host providing SCP service
* @param port TCP port for SCP service
* @param term_func Function to poll during connection for program
* termination, or NULL for none.
* @return Initialised SCP transport
*
* The returned tranport has the is_term member set to term_func.
*/
struct trans *
scp_connect(const char *host, const char *port,
int (*term_func)(void));
/**
* Converts a standard trans connected to an SCP endpoint to an SCP transport
*
* @param trans connected endpoint
* @return != 0 for error
*
* The returned tranport has the is_term member set to term_func.
*/
int
scp_init_trans(struct trans *trans);
/**
* Checks an SCP transport to see if a complete message is
* available for parsing
*
* @param trans SCP transport
* @param[out] available != 0 if a complete message is available
* @return != 0 for error
*/
int
scp_msg_in_check_available(struct trans *trans, int *available);
/**
* Waits on a single transport for an SCP message to be available for
* parsing
*
* @param trans libipm transport
* @return != 0 for error
*
* While the call is active, data-in callbacks for the transport are
* disabled.
*
* Only use this call if you have nothing to do until a message
* arrives on the transport. If you have other transports to service, use
* scp_msg_in_check_available()
*/
int
scp_msg_in_wait_available(struct trans *trans);
/**
* Start parsing an SCP message
*
* @param trans SCP transport
* @return message in the buffer
*
* The results of calling this routine before scp_msg_in_check_available()
* states a message is available are undefined.
*
* Calling this function rests the message parsing pointer to the start
* of the message
*/
enum scp_msg_code
scp_msg_in_start(struct trans *trans);
/**
* Resets an SCP message buffer ready to receive the next message
*
* @param trans libipm transport
*/
void
scp_msg_in_reset(struct trans *trans);
/**
* Send an E_SCP_GATEWAY_REQUEST (SCP client)
*
* @param trans SCP transport
* @param username Username
* @param password Password
* @return != 0 for error
*
* Server replies with E_SCP_GATEWAY_RESPONSE
*/
int
scp_send_gateway_request(struct trans *trans,
const char *username,
const char *password);
/**
* Parse an incoming E_SCP_GATEWAY_REQUEST message (SCP server)
*
* @param trans SCP transport
* @param[out] username Username
* @param[out] password Password
* @return != 0 for error
*/
int
scp_get_gateway_request(struct trans *trans,
const char **username,
const char **password);
/**
* Send an E_SCP_GATEWAY_RESPONSE (SCP server)
*
* @param trans SCP transport
* @param auth_result 0 for success, PAM error code otherwise
* @return != 0 for error
*/
int
scp_send_gateway_response(struct trans *trans,
int auth_result);
/**
* Parses an incoming E_SCP_GATEWAY_RESPONSE (SCP client)
*
* @param trans SCP transport
* @param[out] auth_result 0 for success, PAM error code otherwise
* @return != 0 for error
*/
int
scp_get_gateway_response(struct trans *trans,
int *auth_result);
/* Session messages */
/**
* Send an E_SCP_CREATE_SESSION_REQUEST (SCP client)
*
* @param trans SCP transport
* @param username Username of session to create or re-connect to
* @param password Password for user
* @param type Session type
* @param width Initial session width
* @param height Initial session height
* @param bpp Session bits-per-pixel (ignored for Xorg sessions)
* @param shell User program to run. May be ""
* @param directory Directory to run the program in. May be ""
* @param connection_description Description of the connection
* @return != 0 for error
*
* Server replies with E_SCP_CREATE_SESSION_RESPONSE
*/
int
scp_send_create_session_request(struct trans *trans,
const char *username,
const char *password,
enum scp_session_type type,
unsigned short width,
unsigned short height,
unsigned char bpp,
const char *shell,
const char *directory,
const char *connection_description);
/**
* Parse an incoming E_SCP_CREATE_SESSION_REQUEST (SCP server)
*
* @param trans SCP transport
* @param[out] username Username of session to create or re-connect to
* @param[out] password Password for user
* @param[out] type Session type
* @param[out] width Initial session width
* @param[out] height Initial session height
* @param[out] bpp Session bits-per-pixel (ignored for Xorg sessions)
* @param[out] shell User program to run. May be ""
* @param[out] directory Directory to run the program in. May be ""
* @param[out] connection_description Description of the connection
* @return != 0 for error
*
* Returned string pointers are valid until scp_msg_in_reset() is
* called for the transport
*/
int
scp_get_create_session_request(struct trans *trans,
const char **username,
const char **password,
enum scp_session_type *type,
unsigned short *width,
unsigned short *height,
unsigned char *bpp,
const char **shell,
const char **directory,
const char **connection_description);
/**
* Send an E_SCP_CREATE_SESSION_RESPONSE (SCP server)
*
* @param trans SCP transport
* @param auth_result 0 for success, PAM error code otherwise
* @param display Should be zero if authentication failed.
* @param guid Guid for session. Should be all zeros if authentication failed
*
* @return != 0 for error
*/
int
scp_send_create_session_response(struct trans *trans,
int auth_result,
int display,
const struct guid *guid);
/**
* Parse an incoming E_SCP_CREATE_SESSION_RESPONSE (SCP client)
*
* @param trans SCP transport
* @param[out] auth_result 0 for success, PAM error code otherwise
* @param[out] display Should be zero if authentication failed.
* @param[out] guid Guid for session. Should be all zeros if authentication
* failed
*
* @return != 0 for error
*/
int
scp_get_create_session_response(struct trans *trans,
int *auth_result,
int *display,
struct guid *guid);
/**
* Status of an E_SCP_LIST_SESSIONS_RESPONSE message
*/
enum scp_list_sessions_status
{
/**
* This message contains a valid session, and other messages
* will be sent
*/
E_SCP_LS_SESSION_INFO = 0,
/**
* This message indicates the end of a list of sessions. No session
* is contained in the message */
E_SCP_LS_END_OF_LIST,
/**
* Authentication failed for the user
*/
E_SCP_LS_AUTHENTICATION_FAIL = 100,
/**
* A client-side error occurred allocating memory for the session
*/
E_SCP_LS_NO_MEMORY
};
/**
* Send an E_LIST_SESSIONS_REQUEST (SCP client)
*
* @param trans SCP transport
* @param username Username
* @param password Password
* @return != 0 for error
*
* Server replies with one or more E_SCP_LIST_SESSIONS_RESPONSE
*/
int
scp_send_list_sessions_request(struct trans *trans,
const char *username,
const char *password);
/**
* Parse an incoming E_LIST_SESSIONS_REQUEST (SCP server)
*
* @param trans SCP transport
* @param[out] username Username
* @param[out] password Password
* @return != 0 for error
*/
int
scp_get_list_sessions_request(struct trans *trans,
const char **username,
const char **password);
/**
* Send an E_LIST_SESSIONS_RESPONSE (SCP server)
*
* @param trans SCP transport
* @param status Status of request
* @param info Session to send if status == E_SCL_LS_SESSION_INFO
* @return != 0 for error
*/
int
scp_send_list_sessions_response(
struct trans *trans,
enum scp_list_sessions_status status,
const struct scp_session_info *info);
/**
* Parse an incoming E_LIST_SESSIONS_RESPONSE (SCP client)
*
* @param trans SCP transport
* @param[out] status Status of request
* @param[out] info Session if status == E_SCL_LS_SESSION_INFO
* @return != 0 for error
*
* The session info is returned as a dynamically allocated structure.
* After use the structure (and its members) can be de-allocated with
* a single call to g_free()
*
* The info structures can be added to a 'struct list' with auto_free set.
* When the list is de-allocated, all the structures will be de-allocated too.
*/
int
scp_get_list_sessions_response(
struct trans *trans,
enum scp_list_sessions_status *status,
struct scp_session_info **info);
#endif /* SCP_H */

View File

@ -0,0 +1,65 @@
/**
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) Jay Sorg 2004-2022, all xrdp contributors
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
*
* @file libipm/scp_application_types.h
* @brief scp type declarations intended for use in the application
* @author Simone Fedele/ Matt Burt
*/
#ifndef SCP_APPLICATION_TYPES_H
#define SCP_APPLICATION_TYPES_H
#include <time.h>
/**
* Select the desktop application session type
*/
enum scp_session_type
{
SCP_SESSION_TYPE_XVNC = 0, ///< Session used Xvnc
SCP_SESSION_TYPE_XRDP, ///< Session uses X11rdp
SCP_SESSION_TYPE_XORG ///< Session used Xorg + xorgxrdp
};
#define SCP_SESSION_TYPE_TO_STR(t) \
((t) == SCP_SESSION_TYPE_XVNC ? "Xvnc" : \
(t) == SCP_SESSION_TYPE_XRDP ? "Xrdp" : \
(t) == SCP_SESSION_TYPE_XORG ? "Xorg" : \
"unknown" \
)
/**
* @brief Information to display about a particular sesman session
*/
struct scp_session_info
{
int sid; ///< Session ID
unsigned int display; ///< Display number
enum scp_session_type type; ///< Session type
unsigned short width; ///< Initial session width
unsigned short height; ///< Initial session height
unsigned char bpp; ///< Session bits-per-pixel
time_t start_time; ///< When sesion was created
char *username; ///< Username for session
char *connection_description; ///< Initial connection to session
};
#endif /* SCP_APPLICATION_TYPES_H */