transport.c revision c7bef3b16d3d2a0b09ff75fbbd724283ef1ee7e7
0N/A * The contents of this file are subject to the terms of the 0N/A * Common Development and Distribution License (the "License"). 0N/A * You may not use this file except in compliance with the License. 0N/A * See the License for the specific language governing permissions 0N/A * and limitations under the License. 0N/A * When distributing Covered Code, include this CDDL HEADER in each 0N/A * If applicable, add the following below this CDDL HEADER, with the 0N/A * fields enclosed by brackets "[]" replaced with your own identifying 0N/A * information: Portions Copyright [yyyy] [name of copyright owner] 0N/A * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 0N/A * Use is subject to license terms. 1879N/A * transport layer for audit_remote (handles connection establishment, gss 1879N/A * context initialization, message encryption and verification) 0N/Astatic char *
ver_str =
"01";
/* supported protocol version */ 0N/A * The three locks synchronize the simultaneous actions on top of transmission 0N/A * queue, socket, gss_context. 0N/A/* reset routine synchronization - required by the sending thread */ 0N/A#
define NP_CLOSE -
1 /* notification pipe - close message */ 0N/A#
define NP_EXIT -
2 /* notification pipe - exit message */ 0N/A/* transmission queue helpers */ 0N/A * report_err() - wrapper, mainly due to enhance the code readability - report 0N/A * error to syslog via call to __audit_syslog(). 0N/A * report_gss_err() - GSS API error reporting 0N/A * there is only one version supported by the plugin - "01". 0N/A * Note: connection must be initiated prior version negotiation 0N/A * Set the version proposal string - once we support more than 0N/A * version "01" this part should be extended to solve the concatenation 0N/A * of supported version identifiers. 0N/A * because we support only one version and it is represented by 0N/A * the "01" string. The received version has to be "01" string as well. 0N/A * as an application_data field in the gss_channel_bindings_struct 0N/A * sock_prepare() - creates and connects socket. Function returns 0N/A * reason of failure. 0N/A /* unknown address family */ 0N/A * Note: connection must be established and version negotiated (in plain text) 0N/A * prior to establishing context. 0N/A /* GSS service name = gss_svc_name + "@" + remote hostname (fqdn) */ 0N/A /* initialize channel binding */ 17N/A "Sending init_sec_context token (size=%d)\n",
0N/A * delete_context() - release GSS context. 0N/A * send_token() - send GSS token over the wire. 0N/A * recv_token() - receive GSS token over the wire. 0N/A /* simple DOS prevention mechanism */ 0N/A * connect_timeout() - sets nonblocking I/O on a socket and timeout-connects * send_timeout() - send data (in chunks if needed, each chunk in timeout secs). if (
rc == 0) {
/* timeout */ }
else if (
bytes == 0) {
/* eof */ * recv_timeout() - receive data (in chunks if needed, each chunk in timeout * secs). In case the function is called from receiving thread, the function * cycles the poll() call in timeout seconds (waits for input from server). if (
rc == 0) {
/* timeout */ }
else if (
bytes == 0) {
/* eof */ * read_fd() - reads data of length len from the given file descriptor fd to the * buffer buf, in chunks if needed. Function returns B_FALSE on failure, * otherwise B_TRUE. Function preserves errno, if it was set by the read(2). if (
bytes < 0) {
/* err */ }
else if (
bytes == 0) {
/* eof */ * write_fd() - writes buf of length len to the opened file descriptor fd, in * chunks if needed. The data from the pipe are processed in the receiving * thread. Function returns B_FALSE on failure, otherwise B_TRUE. Function * preserves errno, if it was set by the write(2). if (
bytes == -
1) {
/* err */ * send_record() - send an audit record to a host opening a connection, * negotiate version and establish context if necessary. * We need to grab the reset_lock here, to prevent eventual * unsynchronized cleanup calls within the reset routine (reset caused * by the receiving thread) and the initialization calls in the * send_record() code path. * Check whether the socket was closed by the recv thread prior to call * send_record() and behave accordingly to the reason of the closure. * Send request to other then previously used host. /* initiate the receiving thread */ /* create and connect() socket, negotiate the protocol version */ /* we believe the err_rsn set by sock_prepare() */ /* protocol version negotiation */ "Protocol version negotiation failed\n"));
/* let the socket be initiated for poll() */ /* let the recv thread poll() on the sockfd */ /* if not empty, retransmit contents of the transmission queue */ DPRINT((
dfile,
"Retransmitting remaining (%ld) tokens from " * Concatenate sequence number and the new record. Note, that the * pointer to the chunk of memory allocated for the concatenated values * is later passed to the transq_enqueu() function which stores the * pointer in the transmission queue; subsequently called * transq_dequeue() frees the allocated memory once the MIC is verified * by the recv_record() function. * If we return earlier than the transq_enqueue() is called, it's * necessary to free the in_buf.value explicitly prior to return. /* wrap sequence number and the new record to the per-message token */ }
else {
/* GSS context deleted by the recv thread */ /* enqueue the to-be-sent token into transmission queue */ DPRINT((
dfile,
"Token enqueued for later verification\n"));
* init_recv_record() - initialize the receiver thread * recv_record() - the receiver thread routine * Fill in the information in the vector of file descriptors passed * later on to the poll() function. In the initial state, there is only * one struct pollfd in the vector which contains file descriptor of the * notification pipe - notify_pipe[1]. There might be up to two file * descriptors (struct pollfd) in the vector - notify_pipe[1] which * resides in the vector during the entire life of the receiving thread, * and the own file descriptor from which we read data sent by the * remote server application. * In the endless loop, try to grab some data from the socket or /* block on poll, thus rc != 0 */ /* silently continue on EAGAIN || EINTR */ /* log the debug message in any other case */ * Receive a message from the notification pipe. Information * from the notification pipe takes precedence over the received * data from the remote server application. * Notification pipe message format - message accepted * from the notify pipe comprises of two parts (int || * boolean_t), where if the first part (sizeof (int)) equals * NP_CLOSE, then the second part (sizeof (boolean_t)) signals case NP_EXIT:
/* exit receiving thread */ case NP_CLOSE:
/* close and remove recv_fd */ default:
/* add rc_pipe to the fds */ /* Receive a token from the remote server application */ /* simple DOS prevention mechanism */ * Extract the sequence number and the MIC from * be unique in the transmission queue. Any token in the * transmission queue with the same seq_num as the acknowledge * token received from the server is tested. This is due to the * fact that the plugin cannot influence (in the current * implementation) sequence numbers generated by the kernel (we * are reusing record sequence numbers as a transmission queue * sequence numbers). The probability of having two or more * tokens in the transmission queue is low and at the same time * the performance gain due to using sequence numbers is quite * In case a harder condition with regard to duplicate sequence * numbers in the transmission queue will be desired over time, * the break_flag behavior used below should be * All the GSS_S_OLD_TOKEN, GSS_S_UNSEQ_TOKEN, * GSS_S_GAP_TOKEN are perceived as correct * behavior of the server side. The plugin * implementation is resistant to any of the * above mention cases of returned status codes. "the token (transq len = %ld)\n",
* Both the default case as well as * GSS_S_DUPLICATE_TOKEN case should never * occur. It's been left here for the sake of * If any of the two cases occur, it is * subsequently cought because we don't set * the token_verified flag. }
/* switch (maj_stat) */ }
else {
/* the failure case */ gettext(
"signature verification of the " "received token failed"),
/* retransmission necessary */ "the GSS context expiration\n"));
"detected (seq_num = %lld)\n",
* Received, but unverifiable token is perceived as * the protocol flow corruption with the penalty of * init_poll() - initiates the polling in the receiving thread via sending the * appropriate message over the notify pipe. Message format = (int || * booleant_t), where the first part (sizeof (int)) contains the * part (sizeof (boolean_t)) of the message works only as a padding here and no * action (no recv/send thread synchronisation) is made in the receiving thread * reset_transport() - locked by the reset_lock initiates the reset of socket, * GSS security context and (possibly) flags the transq for retransmission; for * more detailed information see do_reset(). The reset_transport() also allows * the synchronization - waiting for the reset to be finished. * do_close: DO_SYNC, DO_NOT_SYNC * sync_on_return: DO_EXIT (DO_NOT_CLOSE), DO_CLOSE (DO_NOT_EXIT) * Check if the reset routine is in progress or whether it was already * executed by some other thread. * do_reset() - the own reseting routine called from the recv thread. If the * synchronization was requested, signal the finish via conditional variable. /* mark transq to be flushed */ * do_cleanup() - removes all the preallocated space by the plugin; prepares the * allows execution without signalling the successful finish, it's recommended * to use it (we usually want to wait for cleanup before exiting). * note: keeping locking for safety, thought it shouldn't be necessary * in current implementation - we get here only in case the sending code * path calls auditd_plugin_close() (thus no socket manipulation) and * the recv thread is doing the own socket closure. * transq_dequeue() - dequeues given node pointed by the node_ptr from the * transmission queue. Transmission queue should be locked prior to use of this DPRINT((
dfile,
"transq_dequeue(): called with NULL pointer\n"));
/* update the transq_hdr */ * transq_enqueue() - creates new node in (at the end of) the transmission * queue. in_ptoken_ptr is a pointer to the plain token in a form of * gss_buffer_desc. Function returns 0 on success and updates the *node_ptr to * point to a newly added transmission queue node. In case of any failure * function returns 1 and sets the *node_ptr to NULL. * Transmission queue should be locked prior to use of this function. /* value of the seq_token.value = (sequence number || plain token) */ /* update the transq_hdr */ * transq_retransmit() - traverse the transmission queue and try to, 1 by 1, * re-wrap the tokens with the recent context information and retransmit the * tokens from the transmission queue. * Function returns 2 on GSS context expiration, 1 on any other error, 0 on * successfully resent transmission queue. DPRINT((
dfile,
"Retransmission of the remainder in the transqueue\n"));
DPRINT((
dfile,
"Sending transmission queue token (seq=%lld, "