Esempio n. 1
0
SharedPortState::HandlerResult
SharedPortState::HandleFD(Stream *&s)
{
	// Now send the fd.

	ReliSock *sock = static_cast<ReliSock *>(s);

		// The documented way to initialize msghdr is to first set msg_controllen
		// to the size of the cmsghdr buffer and then after initializing
		// cmsghdr(s) to set it to the sum of CMSG_LEN() across all cmsghdrs.

	struct msghdr msg;
	std::vector<char> buf; buf.reserve(CMSG_SPACE(sizeof(int)));
	msg.msg_name = NULL;
	msg.msg_namelen = 0;
	msg.msg_control = &buf[0];
	msg.msg_controllen = CMSG_SPACE(sizeof(int));
	msg.msg_flags = 0;

		// I have found that on MacOS X 10.5, we must send at least 1 byte,
		// or we get "Message too long" when trying to send 0-byte message.
	struct iovec iov[1];
	int junk = 0;
	iov[0].iov_base = &junk;
	iov[0].iov_len = 1;
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;

	struct cmsghdr *cmsg = CMSG_FIRSTHDR((&msg));
	void *cmsg_data = CMSG_DATA(cmsg);
	ASSERT( cmsg && cmsg_data );

	cmsg->cmsg_len = CMSG_LEN(sizeof(int));
	cmsg->cmsg_level = SOL_SOCKET;
	cmsg->cmsg_type = SCM_RIGHTS;

	int fd_to_pass = m_sock->get_file_desc();
	memcpy(cmsg_data,&fd_to_pass,sizeof(int));

	msg.msg_controllen = cmsg->cmsg_len;

#ifdef USE_ABSTRACT_DOMAIN_SOCKET
	//
	// Even if we /can/ use abstract domain sockets, that doesn't meant that
	// we are.  Check the socket's address; if the first byte of its "path"
	// is \0, it's an abstract socket, and we just pass the FD to it.
	// (See 'man 7 unix').
	//
	// Otherwise, it's a socket on-disk; since those are potentially
	// less-secure (depending on filesystem permissions, which need to be lax
	// to permit HTCondor tools like ssh_to_job to work with daemons using
	// CCB), write an audit log entry about where the socket is going.
	//
	// In this construction, the non-error state is in the last if statement,
	// rather than the inner-most.
	//
	struct sockaddr_un addr;
	socklen_t addrlen = sizeof(struct sockaddr_un);
	if( -1 == getpeername( sock->get_file_desc(), (struct sockaddr *) & addr, & addrlen ) ) {
		dprintf( D_AUDIT, *sock, "Failure while auditing connection from %s: unable to obtain domain socket peer address: %s\n",
			m_sock->peer_addr().to_ip_and_port_string().c_str(),
			strerror( errno ) );
	} else if( addrlen <= sizeof( sa_family_t ) ) {
		dprintf( D_AUDIT, *sock, "Failure while auditing connection from %s: unable to obtain domain socket peer address because domain socket peer is unnamed.\n",
			m_sock->peer_addr().to_ip_and_port_string().c_str() );
	} else if( addr.sun_path[0] != '\0' ) {
		struct ucred cred;
		socklen_t len = sizeof(struct ucred);
		int rc = getsockopt( sock->get_file_desc(), SOL_SOCKET, SO_PEERCRED, & cred, & len );
		if( rc == -1 ) {
		dprintf( D_AUDIT, *sock, "Failure while auditing connection via %s from %s: unable to obtain domain socket's peer credentials: %s.\n",
			addr.sun_path,
			m_sock->peer_addr().to_ip_and_port_string().c_str(),
			strerror( errno ) );
		} else {
			std::string procPath;
			formatstr( procPath, "/proc/%d", cred.pid );

			// Needs security review.
			char procExe[1025];
			std::string procExePath = procPath + "/exe";
			ssize_t procExeLength = readlink( procExePath.c_str(), procExe, 1024 );
			if( procExeLength == -1 ) {
				strcpy( procExe, "(readlink failed)" );
			} else if( 0 <= procExeLength && procExeLength <= 1024 ) {
				procExe[procExeLength] = '\0';
			} else {
				procExe[1024] = '\0';
				procExe[1023] = '.';
				procExe[1022] = '.';
				procExe[1021] = '.';
			}

			// Needs security review.
			char procCmdLine[1025];
			std::string procCmdLinePath = procPath + "/cmdline";
			// No _follow, since the kernel doesn't create symlinks for this.
			int pclFD = safe_open_no_create( procCmdLinePath.c_str(), O_RDONLY );
			ssize_t procCmdLineLength = _condor_full_read( pclFD, & procCmdLine, 1024 );
			close( pclFD );
			if( procCmdLineLength == -1 ) {
				strcpy( procCmdLine, "(unable to read cmdline)" );
			} else if( 0 <= procCmdLineLength && procCmdLineLength <= 1024 ) {
				procCmdLine[procCmdLineLength] = '\0';
			} else {
				procCmdLineLength = 1024;
				procCmdLine[1024] = '\0';
				procCmdLine[1023] = '.';
				procCmdLine[1022] = '.';
				procCmdLine[1021] = '.';
			}
			for( unsigned i = 0; i < procCmdLineLength; ++i ) {
				if( procCmdLine[i] == '\0' ) {
					if( procCmdLine[i+1] == '\0' ) { break; }
					procCmdLine[i] = ' ';
				}
			}

			// We can't use m_requested_by because it was supplied by the
			// remote process (and therefore can't be trusted).
			dprintf( D_AUDIT, *sock,
				"Forwarding connection to PID = %d, UID = %d, GID = %d [executable '%s'; command line '%s'] via %s from %s.\n",
				cred.pid, cred.uid, cred.gid,
				procExe,
				procCmdLine,
				addr.sun_path,
				m_sock->peer_addr().to_ip_and_port_string().c_str()
			);
		}
	}
#endif

	if( sendmsg(sock->get_file_desc(),&msg,0) != 1 ) {
		dprintf(D_ALWAYS,"SharedPortClient: failed to pass socket to %s%s: %s\n",
			m_sock_name.c_str(),
			m_requested_by.c_str(),
			strerror(errno));
		return FAILED;
	}

	m_state = RECV_RESP;
	return WAIT;
}