/* 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); } }
/* 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; }