Example #1
0
static int
tcp_connect(void *ctx, int timeout)
{
	struct tcp_ctx *tctx = ctx;
	int error, flags;

	PJDLOG_ASSERT(tctx != NULL);
	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT);
	PJDLOG_ASSERT(tctx->tc_fd >= 0);
	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
	PJDLOG_ASSERT(timeout >= -1);

	flags = fcntl(tctx->tc_fd, F_GETFL);
	if (flags == -1) {
		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
		return (errno);
	}
	/*
	 * We make socket non-blocking so we can handle connection timeout
	 * manually.
	 */
	flags |= O_NONBLOCK;
	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
		pjdlog_common(LOG_DEBUG, 1, errno,
		    "fcntl(F_SETFL, O_NONBLOCK) failed");
		return (errno);
	}

	if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
	    tctx->tc_sa.ss_len) == 0) {
		if (timeout == -1)
			return (0);
		error = 0;
		goto done;
	}
	if (errno != EINPROGRESS) {
		error = errno;
		pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed");
		goto done;
	}
	if (timeout == -1)
		return (0);
	return (tcp_connect_wait(ctx, timeout));
done:
	flags &= ~O_NONBLOCK;
	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
		if (error == 0)
			error = errno;
		pjdlog_common(LOG_DEBUG, 1, errno,
		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
	}
	return (error);
}
Example #2
0
static void
reqlog(int loglevel, int debuglevel, int error, struct hio *hio,
    const char *fmt, ...)
{
	char msg[1024];
	va_list ap;
	int len;

	va_start(ap, fmt);
	len = vsnprintf(msg, sizeof(msg), fmt, ap);
	va_end(ap);
	if ((size_t)len < sizeof(msg)) {
		switch (hio->hio_cmd) {
		case HIO_READ:
			(void)snprintf(msg + len, sizeof(msg) - len,
			    "READ(%ju, %ju).", (uintmax_t)hio->hio_offset,
			    (uintmax_t)hio->hio_length);
			break;
		case HIO_DELETE:
			(void)snprintf(msg + len, sizeof(msg) - len,
			    "DELETE(%ju, %ju).", (uintmax_t)hio->hio_offset,
			    (uintmax_t)hio->hio_length);
			break;
		case HIO_FLUSH:
			(void)snprintf(msg + len, sizeof(msg) - len, "FLUSH.");
			break;
		case HIO_WRITE:
			(void)snprintf(msg + len, sizeof(msg) - len,
			    "WRITE(%ju, %ju).", (uintmax_t)hio->hio_offset,
			    (uintmax_t)hio->hio_length);
			break;
		case HIO_KEEPALIVE:
			(void)snprintf(msg + len, sizeof(msg) - len, "KEEPALIVE.");
			break;
		default:
			(void)snprintf(msg + len, sizeof(msg) - len,
			    "UNKNOWN(%u).", (unsigned int)hio->hio_cmd);
			break;
		}
	}
	pjdlog_common(loglevel, debuglevel, error, "%s", msg);
}
Example #3
0
void
adreq_log(int loglevel, int debuglevel, int error, struct adreq *adreq,
    const char *fmt, ...)
{
	char msg[1024];
	va_list ap;

	va_start(ap, fmt);
	(void)vsnprintf(msg, sizeof(msg), fmt, ap);
	va_end(ap);
	(void)snprlcat(msg, sizeof(msg), "(seq=%ju) ",
	    (uintmax_t)adreq->adr_seq);
	switch (adreq->adr_cmd) {
	case ADIST_CMD_OPEN:
		(void)snprlcat(msg, sizeof(msg), "OPEN(%s)",
		    adreq->adr_data);
		break;
	case ADIST_CMD_APPEND:
		(void)snprlcat(msg, sizeof(msg), "APPEND(%ju)",
		    (uintmax_t)adreq->adr_datasize);
		break;
	case ADIST_CMD_CLOSE:
		(void)snprlcat(msg, sizeof(msg), "CLOSE(%s)",
		    adreq->adr_data);
		break;
	case ADIST_CMD_KEEPALIVE:
		(void)snprlcat(msg, sizeof(msg), "KEEPALIVE");
		break;
	case ADIST_CMD_ERROR:
		(void)snprlcat(msg, sizeof(msg), "ERROR");
		break;
	default:
		(void)snprlcat(msg, sizeof(msg), "UNKNOWN(%hhu)",
		    adreq->adr_cmd);
		break;
	}
	if (error != -1)
		(void)snprlcat(msg, sizeof(msg), ": %s", adist_errstr(error));
	(void)strlcat(msg, ".", sizeof(msg));
	pjdlog_common(loglevel, debuglevel, -1, "%s", msg);
}
Example #4
0
static int
tcp_connect_wait(void *ctx, int timeout)
{
	struct tcp_ctx *tctx = ctx;
	struct timeval tv;
	fd_set fdset;
	socklen_t esize;
	int error, flags, ret;

	PJDLOG_ASSERT(tctx != NULL);
	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT);
	PJDLOG_ASSERT(tctx->tc_fd >= 0);
	PJDLOG_ASSERT(timeout >= 0);

	tv.tv_sec = timeout;
	tv.tv_usec = 0;
again:
	FD_ZERO(&fdset);
	FD_SET(tctx->tc_fd, &fdset);
	ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv);
	if (ret == 0) {
		error = ETIMEDOUT;
		goto done;
	} else if (ret == -1) {
		if (errno == EINTR)
			goto again;
		error = errno;
		pjdlog_common(LOG_DEBUG, 1, errno, "select() failed");
		goto done;
	}
	PJDLOG_ASSERT(ret > 0);
	PJDLOG_ASSERT(FD_ISSET(tctx->tc_fd, &fdset));
	esize = sizeof(error);
	if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error,
	    &esize) == -1) {
		error = errno;
		pjdlog_common(LOG_DEBUG, 1, errno,
		    "getsockopt(SO_ERROR) failed");
		goto done;
	}
	if (error != 0) {
		pjdlog_common(LOG_DEBUG, 1, error,
		    "getsockopt(SO_ERROR) returned error");
		goto done;
	}
	error = 0;
done:
	flags = fcntl(tctx->tc_fd, F_GETFL);
	if (flags == -1) {
		if (error == 0)
			error = errno;
		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
		return (error);
	}
	flags &= ~O_NONBLOCK;
	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
		if (error == 0)
			error = errno;
		pjdlog_common(LOG_DEBUG, 1, errno,
		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
	}
	return (error);
}
Example #5
0
int
sandbox(const char *user, bool capsicum, const char *fmt, ...)
{
#ifdef HAVE_JAIL
	struct jail jailst;
	char *jailhost;
	va_list ap;
#endif
	struct passwd *pw;
	uid_t ruid, euid;
	gid_t rgid, egid;
#ifdef HAVE_GETRESUID
	uid_t suid;
#endif
#ifdef HAVE_GETRESGID
	gid_t sgid;
#endif
	gid_t *groups, *ggroups;
	bool jailed;
	int ngroups, ret;

	PJDLOG_ASSERT(user != NULL);
	PJDLOG_ASSERT(fmt != NULL);

	ret = -1;
	groups = NULL;
	ggroups = NULL;

	/*
	 * According to getpwnam(3) we have to clear errno before calling the
	 * function to be able to distinguish between an error and missing
	 * entry (with is not treated as error by getpwnam(3)).
	 */
	errno = 0;
	pw = getpwnam(user);
	if (pw == NULL) {
		if (errno != 0) {
			pjdlog_errno(LOG_ERR,
			    "Unable to find info about '%s' user", user);
			goto out;
		} else {
			pjdlog_error("'%s' user doesn't exist.", user);
			errno = ENOENT;
			goto out;
		}
	}

	ngroups = sysconf(_SC_NGROUPS_MAX);
	if (ngroups == -1) {
		pjdlog_errno(LOG_WARNING,
		    "Unable to obtain maximum number of groups");
		ngroups = NGROUPS_MAX;
	}
	ngroups++;	/* For base gid. */
	groups = malloc(sizeof(groups[0]) * ngroups);
	if (groups == NULL) {
		pjdlog_error("Unable to allocate memory for %d groups.",
		    ngroups);
		goto out;
	}
	if (getgrouplist(user, pw->pw_gid, groups, &ngroups) == -1) {
		pjdlog_error("Unable to obtain groups of user %s.", user);
		goto out;
	}

#ifdef HAVE_JAIL
	va_start(ap, fmt);
	(void)vasprintf(&jailhost, fmt, ap);
	va_end(ap);
	if (jailhost == NULL) {
		pjdlog_error("Unable to allocate memory for jail host name.");
		goto out;
	}
	bzero(&jailst, sizeof(jailst));
	jailst.version = JAIL_API_VERSION;
	jailst.path = pw->pw_dir;
	jailst.hostname = jailhost;
	if (jail(&jailst) >= 0) {
		jailed = true;
	} else {
		jailed = false;
		pjdlog_errno(LOG_WARNING,
		    "Unable to jail to directory %s", pw->pw_dir);
	}
	free(jailhost);
#else	/* !HAVE_JAIL */
	jailed = false;
#endif	/* !HAVE_JAIL */

	if (!jailed) {
		if (chroot(pw->pw_dir) == -1) {
			pjdlog_errno(LOG_ERR,
			    "Unable to change root directory to %s",
			    pw->pw_dir);
			goto out;
		}
	}
	PJDLOG_VERIFY(chdir("/") == 0);

	if (setgroups(ngroups, groups) == -1) {
		pjdlog_errno(LOG_ERR, "Unable to set groups");
		goto out;
	}
	if (setgid(pw->pw_gid) == -1) {
		pjdlog_errno(LOG_ERR, "Unable to set gid to %u",
		    (unsigned int)pw->pw_gid);
		goto out;
	}
	if (setuid(pw->pw_uid) == -1) {
		pjdlog_errno(LOG_ERR, "Unable to set uid to %u",
		    (unsigned int)pw->pw_uid);
		goto out;
	}

#ifdef HAVE_CAP_ENTER
	if (capsicum) {
		capsicum = (cap_enter() == 0);
		if (!capsicum) {
			pjdlog_common(LOG_DEBUG, 1, errno,
			    "Unable to sandbox using capsicum");
		}
	}
#else	/* !HAVE_CAP_ENTER */
	capsicum = false;
#endif	/* !HAVE_CAP_ENTER */

	/*
	 * Better be sure that everything succeeded.
	 */
#ifdef HAVE_GETRESUID
	PJDLOG_VERIFY(getresuid(&ruid, &euid, &suid) == 0);
	PJDLOG_VERIFY(suid == pw->pw_uid);
#else
	ruid = getuid();
	euid = geteuid();
#endif
	PJDLOG_VERIFY(ruid == pw->pw_uid);
	PJDLOG_VERIFY(euid == pw->pw_uid);
#ifdef HAVE_GETRESGID
	PJDLOG_VERIFY(getresgid(&rgid, &egid, &sgid) == 0);
	PJDLOG_VERIFY(sgid == pw->pw_gid);
#else
	rgid = getgid();
	egid = getegid();
#endif
	PJDLOG_VERIFY(rgid == pw->pw_gid);
	PJDLOG_VERIFY(egid == pw->pw_gid);
	PJDLOG_VERIFY(getgroups(0, NULL) == ngroups);
	ggroups = malloc(sizeof(ggroups[0]) * ngroups);
	if (ggroups == NULL) {
		pjdlog_error("Unable to allocate memory for %d groups.",
		    ngroups);
		goto out;
	}
	PJDLOG_VERIFY(getgroups(ngroups, ggroups) == ngroups);
	qsort(groups, (size_t)ngroups, sizeof(groups[0]), groups_compare);
	qsort(ggroups, (size_t)ngroups, sizeof(ggroups[0]), groups_compare);
	PJDLOG_VERIFY(bcmp(groups, ggroups, sizeof(groups[0]) * ngroups) == 0);

	pjdlog_debug(1,
	    "Privileges successfully dropped using %s%s+setgid+setuid.",
	    capsicum ? "capsicum+" : "", jailed ? "jail" : "chroot");

	ret = 0;
out:
	if (groups != NULL)
		free(groups);
	if (ggroups != NULL)
		free(ggroups);
	return (ret);
}
Example #6
0
int
drop_privs(const struct hast_resource *res)
{
    char jailhost[sizeof(res->hr_name) * 2];
    struct jail jailst;
    struct passwd *pw;
    uid_t ruid, euid, suid;
    gid_t rgid, egid, sgid;
    gid_t gidset[1];
    bool capsicum, jailed;

    /*
     * According to getpwnam(3) we have to clear errno before calling the
     * function to be able to distinguish between an error and missing
     * entry (with is not treated as error by getpwnam(3)).
     */
    errno = 0;
    pw = getpwnam(HAST_USER);
    if (pw == NULL) {
        if (errno != 0) {
            pjdlog_errno(LOG_ERR,
                         "Unable to find info about '%s' user", HAST_USER);
            return (-1);
        } else {
            pjdlog_error("'%s' user doesn't exist.", HAST_USER);
            errno = ENOENT;
            return (-1);
        }
    }

    bzero(&jailst, sizeof(jailst));
    jailst.version = JAIL_API_VERSION;
    jailst.path = pw->pw_dir;
    if (res == NULL) {
        (void)snprintf(jailhost, sizeof(jailhost), "hastctl");
    } else {
        (void)snprintf(jailhost, sizeof(jailhost), "hastd: %s (%s)",
                       res->hr_name, role2str(res->hr_role));
    }
    jailst.hostname = jailhost;
    jailst.jailname = NULL;
    jailst.ip4s = 0;
    jailst.ip4 = NULL;
    jailst.ip6s = 0;
    jailst.ip6 = NULL;
    if (jail(&jailst) >= 0) {
        jailed = true;
    } else {
        jailed = false;
        pjdlog_errno(LOG_WARNING,
                     "Unable to jail to directory to %s", pw->pw_dir);
        if (chroot(pw->pw_dir) == -1) {
            pjdlog_errno(LOG_ERR,
                         "Unable to change root directory to %s",
                         pw->pw_dir);
            return (-1);
        }
    }
    PJDLOG_VERIFY(chdir("/") == 0);
    gidset[0] = pw->pw_gid;
    if (setgroups(1, gidset) == -1) {
        pjdlog_errno(LOG_ERR, "Unable to set groups to gid %u",
                     (unsigned int)pw->pw_gid);
        return (-1);
    }
    if (setgid(pw->pw_gid) == -1) {
        pjdlog_errno(LOG_ERR, "Unable to set gid to %u",
                     (unsigned int)pw->pw_gid);
        return (-1);
    }
    if (setuid(pw->pw_uid) == -1) {
        pjdlog_errno(LOG_ERR, "Unable to set uid to %u",
                     (unsigned int)pw->pw_uid);
        return (-1);
    }

#ifdef HAVE_CAPSICUM
    capsicum = (cap_enter() == 0);
    if (!capsicum) {
        pjdlog_common(LOG_DEBUG, 1, errno,
                      "Unable to sandbox using capsicum");
    } else if (res != NULL) {
        cap_rights_t rights;
        static const unsigned long geomcmds[] = {
            DIOCGDELETE,
            DIOCGFLUSH
        };

        PJDLOG_ASSERT(res->hr_role == HAST_ROLE_PRIMARY ||
                      res->hr_role == HAST_ROLE_SECONDARY);

        cap_rights_init(&rights, CAP_FLOCK, CAP_IOCTL, CAP_PREAD,
                        CAP_PWRITE);
        if (cap_rights_limit(res->hr_localfd, &rights) == -1) {
            pjdlog_errno(LOG_ERR,
                         "Unable to limit capability rights on local descriptor");
        }
        if (cap_ioctls_limit(res->hr_localfd, geomcmds,
                             sizeof(geomcmds) / sizeof(geomcmds[0])) == -1) {
            pjdlog_errno(LOG_ERR,
                         "Unable to limit allowed GEOM ioctls");
        }

        if (res->hr_role == HAST_ROLE_PRIMARY) {
            static const unsigned long ggatecmds[] = {
                G_GATE_CMD_MODIFY,
                G_GATE_CMD_START,
                G_GATE_CMD_DONE,
                G_GATE_CMD_DESTROY
            };

            cap_rights_init(&rights, CAP_IOCTL);
            if (cap_rights_limit(res->hr_ggatefd, &rights) == -1) {
                pjdlog_errno(LOG_ERR,
                             "Unable to limit capability rights to CAP_IOCTL on ggate descriptor");
            }
            if (cap_ioctls_limit(res->hr_ggatefd, ggatecmds,
                                 sizeof(ggatecmds) / sizeof(ggatecmds[0])) == -1) {
                pjdlog_errno(LOG_ERR,
                             "Unable to limit allowed ggate ioctls");
            }
        }
    }
#else
    capsicum = false;
#endif

    /*
     * Better be sure that everything succeeded.
     */
    PJDLOG_VERIFY(getresuid(&ruid, &euid, &suid) == 0);
    PJDLOG_VERIFY(ruid == pw->pw_uid);
    PJDLOG_VERIFY(euid == pw->pw_uid);
    PJDLOG_VERIFY(suid == pw->pw_uid);
    PJDLOG_VERIFY(getresgid(&rgid, &egid, &sgid) == 0);
    PJDLOG_VERIFY(rgid == pw->pw_gid);
    PJDLOG_VERIFY(egid == pw->pw_gid);
    PJDLOG_VERIFY(sgid == pw->pw_gid);
    PJDLOG_VERIFY(getgroups(0, NULL) == 1);
    PJDLOG_VERIFY(getgroups(1, gidset) == 1);
    PJDLOG_VERIFY(gidset[0] == pw->pw_gid);

    pjdlog_debug(1,
                 "Privileges successfully dropped using %s%s+setgid+setuid.",
                 capsicum ? "capsicum+" : "", jailed ? "jail" : "chroot");

    return (0);
}