Hello
I’m trying to call the sendmsg() system call from ruby in a way that
allocates space for the kernel to set the user credentials in the
appropriate structures (this is in FreeBSD i386).
I’m using the code below, which gives me an EFAULT (bad address) error,
and I cannot see what I’m doing wrong here… any help would be awesome.
The equivalent C code (which works) follows after the ruby attempt.
def pointer(buf)
[buf].pack(“P”).unpack(“L_”).first
end
def sendmsg(fd, buf)
iov = [buf, buf.length].pack(“PI_”)
cmsg_len = 96 # CMSG_LEN(sizeof(struct cmsgcred))
cmsg_space = 96 # CMSG_SPACE(sizeof(struct cmsgcred))
cmsg_level = 0xffff # SOL_SOCKET
cmsg_type = 0x03 # SCM_CREDS
cmsg_data = ["\x00" * cmsg_space].pack("P")
cmsghdr = [
cmsg_len,
cmsg_level,
cmsg_type,
cmsg_data
].pack("I_i_i_P")
msg_control_ptr = pointer(cmsghdr)
msg_controllen = cmsg_space
msghdr = [
0, # msg_name
0, # msg_namelen
pointer(iov), # msg_iov
1, # msg_iovlen
pointer(cmsghdr), # msg_control
cmsg_space, # msg_controllen
0 # msg_flags
].pack("L_i_L_i_L_i_i_")
syscall(28, pointer(msghdr), 0) # 28 is sendmsg()
end
Here’s the C snippet:
union {
struct cmsghdr hdr;
char cred[CMSG_SPACE(sizeof(struct cmsgcred))];
} cmsg;
struct iovec iov[1];
struct msghdr msg;
iov[0].iov_base = (void *)buf;
iov[0].iov_len = count;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = (caddr_t)&cmsg;
msg.msg_controllen = CMSG_SPACE(sizeof(struct cmsgcred));
memset(&cmsg, 0, sizeof(cmsg));
cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(struct cmsgcred));
cmsg.hdr.cmsg_level = SOL_SOCKET;
cmsg.hdr.cmsg_type = SCM_CREDS;
sendmsg(fd, &msg, 0);
Thanks in advance,
Andre