Exemple #1
0
/* replacement function for open, fails if fn is a symbolic links */
int safe_open_wrapper(const char *fn, int flags, mode_t mode)
{
    if (flags & O_CREAT) {
	/* O_CREAT specified, pick function based on O_EXCL flags */
			  
	if (flags & O_EXCL) {
	    return safe_create_fail_if_exists(fn, flags, mode);
	}  else  {
	    return safe_create_keep_if_exists(fn, flags, mode);
	}
    } else {
	/* O_CREAT not specified */
	return safe_open_no_create(fn, flags);
    }
}
Exemple #2
0
/* create file if it doesn't exist, keep inode if it does */
int safe_create_keep_if_exists(const char *fn, int flags, mode_t mode)
{
    int f = -1;
    int saved_errno = errno;
    int num_tries = 0;

    /* check for invalid argument values */
    if (!fn)  {
	errno = EINVAL;
	return -1;
    }

    /* Remove O_CREATE and O_EXCL from the flags, the safe_open_no_create()
     * requires them to not be included and safe_creat_fail_if_exists() adds
     * them implicitly.
     */
    flags &= ~O_CREAT & ~O_EXCL;

    /* Loop alternating between creating the file (and failing if it exists)
     * and opening an existing file.  Return an error if any error occurs other
     * than an indication that the other open method should work.
     */
    while (f == -1)  {
	/* If this is the second or subsequent attempt, then someone is
	 * manipulating the file system object referred to by fn.  Call the user
	 * defined callback if registered, and fail if it returns a non-zero value.
	 */
	if (++num_tries > 1)  {
	    /* the default error is EAGAIN, the callback function may change this */
	    errno = EAGAIN;
	    if (safe_open_path_warning(fn) != 0)  {
		return -1;
	    }

	    /* check if we tried too many times */
	    if (num_tries > SAFE_OPEN_RETRY_MAX)  {
		/* let the user decide what to do */
		return -1;
	    }
	}

	f = safe_open_no_create(fn, flags);

	/* check for error */
	if (f == -1)  {
	    if (errno != ENOENT)  {
		return -1;
	    }

	    /* previous function said the file exists, so this should work */
	    f = safe_create_fail_if_exists(fn, flags, mode);
	    if (f == -1 && errno != EEXIST)  {
		return -1;
	    }

	    /* At this point, safe_open_no_create either worked in which case
	     * we are done, or it failed saying the file does not exist in which
	     * case we'll take another spin in the loop.
	     */
	}
    }

    /* no error, restore errno in case we had recoverable failures */
    errno = saved_errno;

    return f;
}
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;
}