static size_t fill_buffer_from_fd(const int fd, void * const dst, const size_t buf_size) { char *buf = dst; size_t pos = 0; RUNTIME_ASSERT(buf); RUNTIME_ASSERT(buf_size > 0); RUNTIME_ASSERT((size_t) -1 != buf_size); while (pos < buf_size) { ssize_t ret; size_t size; size = buf_size - pos; ret = read(fd, &buf[pos], size); if ((ssize_t) -1 == ret) { if (!is_temporary_error(errno) || wait_for_fd(fd) < 0) { return -1; } } else if (0 == ret) { if (pos != 0) { errno = EIO; return -1; } return 0; /* EOF */ } else { RUNTIME_ASSERT((size_t) ret <= size); pos += (size_t) ret; } } return 1; }
MINDY_NORETURN void wait_for_output(struct thread *thread, int fd, MINDY_NORETURN void (*advance)(struct thread *thread)) { wait_for_fd(thread, fd, &Writers, advance); }
static int write_timeout(int fd, const void *buf, int len) { int n = 0, rc = 0; const char *b = buf; while(n < len) { rc = write(fd, b + n, len - n); if(rc < 0) { if(errno == EAGAIN || errno == EINTR) { rc = wait_for_fd(1, fd, 100); if(rc > 0) { rc = write(fd, b + n, len - n); } } } if(rc > 0) n += rc; else break; } if(n >= len) return 1; else { if(rc >= 0) errno = EAGAIN; return -1; } }
int babel_send(int s, void *buf1, int buflen1, void *buf2, int buflen2, struct sockaddr *sin, int slen) { struct iovec iovec[2]; struct msghdr msg; int rc; iovec[0].iov_base = buf1; iovec[0].iov_len = buflen1; iovec[1].iov_base = buf2; iovec[1].iov_len = buflen2; memset(&msg, 0, sizeof(msg)); msg.msg_name = (struct sockaddr*)sin; msg.msg_namelen = slen; msg.msg_iov = iovec; msg.msg_iovlen = 2; again: rc = sendmsg(s, &msg, 0); if(rc < 0) { if(errno == EINTR) goto again; else if(errno == EAGAIN) { int rc2; rc2 = wait_for_fd(1, s, 5); if(rc2 > 0) goto again; errno = EAGAIN; } } return rc; }
static int netlink_talk(struct nlmsghdr *nh) { int rc; struct sockaddr_nl nladdr; struct msghdr msg; struct iovec iov; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = 0; memset(&msg, 0, sizeof(msg)); msg.msg_name = &nladdr; msg.msg_namelen = sizeof(nladdr); msg.msg_iov = &iov; msg.msg_iovlen = 1; iov.iov_base = nh; iov.iov_len = nh->nlmsg_len; nh->nlmsg_flags |= NLM_F_ACK; nh->nlmsg_seq = ++nl_command.seqno; rc = sendmsg(nl_command.sock, &msg, 0); if(rc < 0 && (errno == EAGAIN || errno == EINTR)) { rc = wait_for_fd(1, nl_command.sock, 100); if(rc <= 0) { if(rc == 0) errno = EAGAIN; } else { rc = sendmsg(nl_command.sock, &msg, 0); } } if(rc < nh->nlmsg_len) { int saved_errno = errno; perror("sendmsg"); errno = saved_errno; return -1; } rc = netlink_read(&nl_command, NULL, 1, NULL, NULL); /* ACK */ return rc; }
static int netlink_send(struct nlmsghdr *nh, int sock) { int rc; struct sockaddr_nl nladdr; struct msghdr msg; struct iovec iov; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = 0; memset(&msg, 0, sizeof(msg)); msg.msg_name = &nladdr; msg.msg_namelen = sizeof(nladdr); msg.msg_iov = &iov; msg.msg_iovlen = 1; iov.iov_base = nh; iov.iov_len = nh->nlmsg_len; nh->nlmsg_flags |= NLM_F_ACK; nh->nlmsg_seq = ++nl_seqno; pending_ack = nl_seqno; rc = sendmsg(sock, &msg, 0); if(rc < 0 && (errno == EAGAIN || errno == EINTR)) { rc = wait_for_fd(1, sock, 100); if(rc <= 0) { if(rc == 0) errno = EAGAIN; } else { rc = sendmsg(sock, &msg, 0); } } if(rc < nh->nlmsg_len) { log_msg(LOG_PERROR, "sendmsg"); return -1; } return rc; }
static int netlink_read_ack(int sock) { int rc, again = 0; assert(pending_ack >= 0); again: rc = netlink_read_all(sock); if(!again && rc >= 0 && pending_ack >= 0) { rc = wait_for_fd(1, nl_socket, 100); if(rc <= 0) { if(rc == 0) errno = EAGAIN; } else { again = 1; goto again; } } return pending_ack < 0 ? 0 : -1; }
static void ssl_recv_tcp_send(SSL *recvssl, int sendfd) { static unsigned char buf[65536]; unsigned char *ptr; ssize_t tcpdone; size_t todo; int recvfd, ssldone; recvfd = SSL_get_fd(recvssl); PJDLOG_ASSERT(recvfd >= 0); pjdlog_debug(2, "%s: start %d -> %d", __func__, recvfd, sendfd); for (;;) { ssldone = SSL_read(recvssl, buf, sizeof(buf)); pjdlog_debug(2, "%s: SSL_read() returned %d", __func__, ssldone); if (ssl_check_error(recvssl, ssldone) == -1) break; todo = (size_t)ssldone; ptr = buf; do { tcpdone = send(sendfd, ptr, todo, MSG_NOSIGNAL); pjdlog_debug(2, "%s: send() returned %zd", __func__, tcpdone); if (tcpdone == 0) { pjdlog_debug(1, "Connection terminated."); exit(0); } else if (tcpdone == -1) { if (errno == EINTR || errno == ENOBUFS) continue; if (errno == EAGAIN) { (void)wait_for_fd(sendfd, -1); continue; } pjdlog_exit(EX_TEMPFAIL, "send() failed"); } todo -= tcpdone; ptr += tcpdone; } while (todo > 0); } pjdlog_debug(2, "%s: done %d -> %d", __func__, recvfd, sendfd); }
int babel_send(int s, const void *buf1, int buflen1, const void *buf2, int buflen2, const struct sockaddr *sin, int slen) { struct iovec iovec[2]; struct msghdr msg; int rc, count = 0; iovec[0].iov_base = (void*)buf1; iovec[0].iov_len = buflen1; iovec[1].iov_base = (void*)buf2; iovec[1].iov_len = buflen2; memset(&msg, 0, sizeof(msg)); msg.msg_name = (struct sockaddr*)sin; msg.msg_namelen = slen; msg.msg_iov = iovec; msg.msg_iovlen = 2; /* The Linux kernel can apparently keep returning EAGAIN indefinitely. */ again: rc = sendmsg(s, &msg, 0); if(rc < 0) { if(errno == EINTR) { count++; if(count < 100) goto again; } else if(errno == EAGAIN) { int rc2; rc2 = wait_for_fd(1, s, 5); if(rc2 > 0) { count++; if(count < 100) goto again; } errno = EAGAIN; } } return rc; }
static int tls_connect_wait(void *ctx, int timeout) { struct tls_ctx *tlsctx = ctx; int error, sockfd; uint8_t connected; PJDLOG_ASSERT(tlsctx != NULL); PJDLOG_ASSERT(tlsctx->tls_magic == TLS_CTX_MAGIC); PJDLOG_ASSERT(tlsctx->tls_side == TLS_SIDE_CLIENT); PJDLOG_ASSERT(tlsctx->tls_sock != NULL); PJDLOG_ASSERT(!tlsctx->tls_wait_called); PJDLOG_ASSERT(timeout >= 0); sockfd = proto_descriptor(tlsctx->tls_sock); error = wait_for_fd(sockfd, timeout); if (error != 0) return (error); for (;;) { switch (recv(sockfd, &connected, sizeof(connected), MSG_WAITALL)) { case -1: if (errno == EINTR || errno == ENOBUFS) continue; error = errno; break; case 0: pjdlog_debug(1, "Connection terminated."); error = ENOTCONN; break; case 1: tlsctx->tls_wait_called = true; break; } break; } return (error); }
static void tcp_recv_ssl_send(int recvfd, SSL *sendssl) { static unsigned char buf[65536]; ssize_t tcpdone; int sendfd, ssldone; sendfd = SSL_get_fd(sendssl); PJDLOG_ASSERT(sendfd >= 0); pjdlog_debug(2, "%s: start %d -> %d", __func__, recvfd, sendfd); for (;;) { tcpdone = recv(recvfd, buf, sizeof(buf), 0); pjdlog_debug(2, "%s: recv() returned %zd", __func__, tcpdone); if (tcpdone == 0) { pjdlog_debug(1, "Connection terminated."); exit(0); } else if (tcpdone == -1) { if (errno == EINTR) continue; else if (errno == EAGAIN) break; pjdlog_exit(EX_TEMPFAIL, "recv() failed"); } for (;;) { ssldone = SSL_write(sendssl, buf, (int)tcpdone); pjdlog_debug(2, "%s: send() returned %d", __func__, ssldone); if (ssl_check_error(sendssl, ssldone) == -1) { (void)wait_for_fd(sendfd, -1); continue; } PJDLOG_ASSERT(ssldone == tcpdone); break; } } pjdlog_debug(2, "%s: done %d -> %d", __func__, recvfd, sendfd); }
static void tls_exec_client(const char *user, int startfd, const char *srcaddr, const char *dstaddr, const char *fingerprint, const char *defport, int timeout, int debuglevel) { struct proto_conn *tcp; char *saddr, *daddr; SSL_CTX *sslctx; SSL *ssl; long ret; int sockfd, tcpfd; uint8_t connected; pjdlog_debug_set(debuglevel); pjdlog_prefix_set("[TLS sandbox] (client) "); #ifdef HAVE_SETPROCTITLE setproctitle("[TLS sandbox] (client) "); #endif proto_set("tcp:port", defport); sockfd = startfd; /* Change tls:// to tcp://. */ if (srcaddr == NULL) { saddr = NULL; } else { saddr = strdup(srcaddr); if (saddr == NULL) pjdlog_exitx(EX_TEMPFAIL, "Unable to allocate memory."); bcopy("tcp://", saddr, 6); } daddr = strdup(dstaddr); if (daddr == NULL) pjdlog_exitx(EX_TEMPFAIL, "Unable to allocate memory."); bcopy("tcp://", daddr, 6); /* Establish TCP connection. */ if (proto_connect(saddr, daddr, timeout, &tcp) == -1) exit(EX_TEMPFAIL); SSL_load_error_strings(); SSL_library_init(); /* * TODO: On FreeBSD we could move this below sandbox() once libc and * libcrypto use sysctl kern.arandom to obtain random data * instead of /dev/urandom and friends. */ sslctx = SSL_CTX_new(TLSv1_client_method()); if (sslctx == NULL) pjdlog_exitx(EX_TEMPFAIL, "SSL_CTX_new() failed."); if (sandbox(user, true, "proto_tls client: %s", dstaddr) != 0) pjdlog_exitx(EX_CONFIG, "Unable to sandbox TLS client."); pjdlog_debug(1, "Privileges successfully dropped."); SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); /* Load CA certs. */ /* TODO */ //SSL_CTX_load_verify_locations(sslctx, cacerts_file, NULL); ssl = SSL_new(sslctx); if (ssl == NULL) pjdlog_exitx(EX_TEMPFAIL, "SSL_new() failed."); tcpfd = proto_descriptor(tcp); block(tcpfd); if (SSL_set_fd(ssl, tcpfd) != 1) pjdlog_exitx(EX_TEMPFAIL, "SSL_set_fd() failed."); ret = SSL_connect(ssl); ssl_check_error(ssl, (int)ret); nonblock(sockfd); nonblock(tcpfd); tls_certificate_verify(ssl, fingerprint); /* * The following byte is send to make proto_connect_wait() to work. */ connected = 1; for (;;) { switch (send(sockfd, &connected, sizeof(connected), 0)) { case -1: if (errno == EINTR || errno == ENOBUFS) continue; if (errno == EAGAIN) { (void)wait_for_fd(sockfd, -1); continue; } pjdlog_exit(EX_TEMPFAIL, "send() failed"); case 0: pjdlog_debug(1, "Connection terminated."); exit(0); case 1: break; } break; } tls_loop(sockfd, ssl); }
static int netlink_read(struct netlink *nl, struct netlink *nl_ignore, int answer, int (*fn)(struct nlmsghdr *nh, void *data), void *data) { /* 'answer' must be true when we just have send a request on 'nl_socket' */ /* 'nl_ignore' is used in kernel_callback to ignore message originating */ /* from 'nl_command' while reading 'nl_listen' */ /* Return code : */ /* -1 : error */ /* 0 : if(fn) found_interesting; else found_ack; */ /* 1 : only if(fn) nothing interesting has been found */ /* 2 : nothing found, retry */ int err; struct msghdr msg; struct sockaddr_nl nladdr; struct iovec iov; struct nlmsghdr *nh; int len; int interesting = 0; int done = 0; char buf[8192]; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; memset(&msg, 0, sizeof(msg)); msg.msg_name = &nladdr; msg.msg_namelen = sizeof(nladdr); msg.msg_iov = &iov; msg.msg_iovlen = 1; iov.iov_base = &buf; do { iov.iov_len = sizeof(buf); len = recvmsg(nl->sock, &msg, 0); if(len < 0 && (errno == EAGAIN || errno == EINTR)) { int rc; rc = wait_for_fd(0, nl->sock, 100); if(rc <= 0) { if(rc == 0) errno = EAGAIN; } else { len = recvmsg(nl->sock, &msg, 0); } } if(len < 0) { perror("netlink_read: recvmsg()"); return 2; } else if(len == 0) { fprintf(stderr, "netlink_read: EOF\n"); goto socket_error; } else if(msg.msg_namelen != nl->socklen) { fprintf(stderr, "netlink_read: unexpected sender address length (%d)\n", msg.msg_namelen); goto socket_error; } else if(nladdr.nl_pid != 0) { kdebugf("netlink_read: message not sent by kernel.\n"); return 2; } kdebugf("Netlink message: "); for(nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) { kdebugf("%s", (nh->nlmsg_flags & NLM_F_MULTI) ? "[multi] " : ""); if(!answer) done = 1; if(nl_ignore && nh->nlmsg_pid == nl_ignore->sockaddr.nl_pid) { kdebugf("(ignore), "); continue; } else if(answer && (nh->nlmsg_pid != nl->sockaddr.nl_pid || nh->nlmsg_seq != nl->seqno)) { kdebugf("(wrong seqno %d %d /pid %d %d), ", nh->nlmsg_seq, nl->seqno, nh->nlmsg_pid, nl->sockaddr.nl_pid); continue; } else if(nh->nlmsg_type == NLMSG_DONE) { kdebugf("(done)\n"); done = 1; break; } else if(nh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nh); if(err->error == 0) { kdebugf("(ACK)\n"); return 0; } else { kdebugf("netlink_read: %s\n", strerror(-err->error)); errno = -err->error; return -1; } } else if(fn) { kdebugf("(msg -> \""); err = fn(nh,data); kdebugf("\" %d), ", err); if(err < 0) return err; interesting = interesting || err; continue; } kdebugf(", "); } kdebugf("\n"); if(msg.msg_flags & MSG_TRUNC) fprintf(stderr, "netlink_read: message truncated\n"); } while (!done); return interesting; socket_error: close(nl->sock); nl->sock = -1; errno = EIO; return -1; }