diff --git a/libipm/libipm.c b/libipm/libipm.c index 953d9893..70aed3d8 100644 --- a/libipm/libipm.c +++ b/libipm/libipm.c @@ -32,6 +32,23 @@ const char *libipm_valid_type_chars = "ybnqiuxtsdhogB"; +/*****************************************************************************/ +void +libipm_msg_in_close_file_descriptors(struct trans *self) +{ + struct libipm_priv *priv = (struct libipm_priv *)self->extra_data; + if (priv != NULL) + { + unsigned int i; + for (i = priv->in_fd_index ; i < priv->in_fd_count; ++i) + { + g_file_close(priv->in_fds[i]); + } + priv->in_fd_count = 0; + priv->in_fd_index = 0; + } +} + /**************************************************************************//** * Send function for a struct trans initialised with libipm_init_trans() * @@ -60,6 +77,52 @@ libipm_trans_send_proc(struct trans *self, const char *data, int len) return rv; } +/**************************************************************************//** + * Receive function for a struct trans initialised with libipm_init_trans() + * + * @param trans Transport to receive on + * @param data pointer to receive data buffer + * @param len Length of data to read + * @return As for read(2) + */ +static int +libipm_trans_recv_proc(struct trans *self, char *data, int len) +{ + int rv; + struct libipm_priv *priv = (struct libipm_priv *)self->extra_data; + if (priv != NULL && data == self->in_s->data) + { + /* We're receiving the message header */ + unsigned int fdcount; + + /* Check there are no live file descriptors in the input + * buffer from a previous message. This shouldn't happen, but + * if by some chance it does, we could leak file descriptors */ + if (priv->in_fd_count > 0) + { + LOG(LOG_LEVEL_WARNING, "Unconsumed file descriptors detected"); + libipm_msg_in_close_file_descriptors(self); + } + rv = g_sck_recv_fd_set(self->sck, data, len, + priv->in_fds, MAX_FD_PER_MSG, + &fdcount); + if (fdcount > MAX_FD_PER_MSG) + { + LOG(LOG_LEVEL_WARNING, + "%d file descriptors were discarded on recvmsg()", + fdcount - MAX_FD_PER_MSG); + fdcount = MAX_FD_PER_MSG; + } + priv->in_fd_count = fdcount; + } + else + { + rv = g_sck_recv(self->sck, data, len, 0); + } + + return rv; +} + /**************************************************************************//** * Destructor for a struct trans initialised with libipm_init_trans() @@ -71,6 +134,7 @@ libipm_trans_destructor(struct trans *trans) { struct libipm_priv *priv = (struct libipm_priv *)trans->extra_data; + libipm_msg_in_close_file_descriptors(trans); if (priv != NULL) { if ((priv->flags & LIBIPM_E_MSG_IN_ERASE_AFTER_USE) != 0 && @@ -113,6 +177,7 @@ libipm_init_trans(struct trans *trans, priv->msgno_to_str = msgno_to_str; trans->trans_send = libipm_trans_send_proc; + trans->trans_recv = libipm_trans_recv_proc; trans->extra_data = priv; trans->extra_destructor = libipm_trans_destructor; diff --git a/libipm/libipm.h b/libipm/libipm.h index 8c3fbdd7..6f9ace7e 100644 --- a/libipm/libipm.h +++ b/libipm/libipm.h @@ -300,8 +300,8 @@ libipm_msg_in_peek_type(struct trans *trans); * x | int64_t * | Signed (two's complement) 64-bit integer * t | uint64_t * | Unsigned 64-bit integer * s | char ** | NULL-terminated string + * h | int * | File descriptor * d | - | (reserved) - * h | - | (reserved) * o | - | (reserved) * g | - | (reserved) * @@ -315,6 +315,9 @@ libipm_msg_in_peek_type(struct trans *trans); * returned. This pointer will only be valid until the next call to * libipm_msg_in_reset() * + * The 'h' type can only be used where the underlying transport is a + * UNIX domain socket. + * * 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 @@ -330,6 +333,9 @@ libipm_msg_in_parse(struct trans *trans, const char *format, ...); * 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 * + * Any file descriptors received from the other end but not parsed + * by the application are closed. + * * @param trans libipm transport */ void diff --git a/libipm/libipm_private.h b/libipm/libipm_private.h index bb8c8ef3..d82c00c4 100644 --- a/libipm/libipm_private.h +++ b/libipm/libipm_private.h @@ -61,6 +61,11 @@ struct libipm_priv int out_fds[MAX_FD_PER_MSG]; unsigned short in_msgno; unsigned short in_param_count; + /** Pointer to next fd to be consumed by the app */ + unsigned short in_fd_index; + /** Number of fds in the incoming message */ + unsigned short in_fd_count; + int in_fds[MAX_FD_PER_MSG]; }; /** @@ -71,4 +76,15 @@ struct libipm_priv */ extern const char *libipm_valid_type_chars; +/** + * Close input file descriptors for an input message + * + * If file descriptors are read from the other end, but not passed to the + * application, they must be closed to prevent file descriptor leaks + * + * @param trans Transport to close file descriptors for + */ +void +libipm_msg_in_close_file_descriptors(struct trans *self); + #endif /* LIBIPM__PRIVATE_H */ diff --git a/libipm/libipm_recv.c b/libipm/libipm_recv.c index 4f907a03..5842a1e0 100644 --- a/libipm/libipm_recv.c +++ b/libipm/libipm_recv.c @@ -460,6 +460,36 @@ extract_char_ptr_type(char c, struct trans *trans, va_list *argptr) return rv; } + +/**************************************************************************//** + * Extract a file descriptor 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_fd_type(char c, struct trans *trans, va_list *argptr) +{ + enum libipm_status rv = E_LI_SUCCESS; + struct libipm_priv *priv = (struct libipm_priv *)trans->extra_data; + + /* File descriptor available? */ + if (priv->in_fd_index >= priv->in_fd_count) + { + log_parse_error(trans, "No file descriptors available"); + rv = E_LI_TOO_MANY_FDS; + } + else + { + int *tmp = va_arg(*argptr, int *); + + *tmp = priv->in_fds[priv->in_fd_index++]; + } + return rv; +} + /**************************************************************************//** * Extract a fixed size block from the input stream * @@ -588,6 +618,10 @@ libipm_msg_in_parsev(struct trans *trans, const char *format, va_list *argptr) rv = extract_char_ptr_type(c, trans, argptr); break; + case 'h': + rv = extract_fd_type(c, trans, argptr); + break; + case 'B': rv = extract_fsb_type(c, trans, argptr); break; @@ -650,6 +684,7 @@ libipm_msg_in_reset(struct trans *trans) } priv->in_msgno = 0; priv->in_param_count = 0; + libipm_msg_in_close_file_descriptors(trans); } trans->extra_flags = 0;