Пример #1
0
static void
tls_remote_address(const void *ctx, char *addr, size_t size)
{
	const struct tls_ctx *tlsctx = ctx;

	PJDLOG_ASSERT(tlsctx != NULL);
	PJDLOG_ASSERT(tlsctx->tls_magic == TLS_CTX_MAGIC);
	PJDLOG_ASSERT(tlsctx->tls_wait_called);

	switch (tlsctx->tls_side) {
	case TLS_SIDE_CLIENT:
		PJDLOG_ASSERT(tlsctx->tls_sock != NULL);

		PJDLOG_VERIFY(strlcpy(addr, "tls://N/A", size) < size);
		break;
	case TLS_SIDE_SERVER_WORK:
		PJDLOG_ASSERT(tlsctx->tls_sock != NULL);

		PJDLOG_VERIFY(strlcpy(addr, tlsctx->tls_raddr, size) < size);
		break;
	case TLS_SIDE_SERVER_LISTEN:
		PJDLOG_ASSERT(tlsctx->tls_tcp != NULL);

		proto_remote_address(tlsctx->tls_tcp, addr, size);
		PJDLOG_ASSERT(strncmp(addr, "tcp://", 6) == 0);
		/* Replace tcp:// prefix with tls:// */
		bcopy("tls://", addr, 6);
		break;
	default:
		PJDLOG_ABORT("Invalid side (%d).", tlsctx->tls_side);
	}
}
Пример #2
0
static void
tcp_remote_address(const void *ctx, char *addr, size_t size)
{
	const struct tcp_ctx *tctx = ctx;
	struct sockaddr_storage sa;
	socklen_t salen;

	PJDLOG_ASSERT(tctx != NULL);
	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);

	salen = sizeof(sa);
	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa, &salen) == -1) {
		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
		return;
	}
	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
}
Пример #3
0
static void
uds_remote_address(const void *ctx, char *addr, size_t size)
{
	const struct uds_ctx *uctx = ctx;
	struct sockaddr_un sun;
	socklen_t sunlen;

	PJDLOG_ASSERT(uctx != NULL);
	PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
	PJDLOG_ASSERT(addr != NULL);

	sunlen = sizeof(sun);
	if (getpeername(uctx->uc_fd, (struct sockaddr *)&sun, &sunlen) == -1) {
		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
		return;
	}
	PJDLOG_ASSERT(sun.sun_family == AF_UNIX);
	if (sun.sun_path[0] == '\0') {
		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
		return;
	}
	snprintf(addr, size, "uds://%s", sun.sun_path);
}
Пример #4
0
static void
tls_certificate_verify(SSL *ssl, const char *fingerprint)
{
	unsigned char md[EVP_MAX_MD_SIZE];
	char mdstr[sizeof("SHA256=") - 1 + EVP_MAX_MD_SIZE * 3];
	char *mdstrp;
	unsigned int i, mdsize;
	X509 *cert;

	if (fingerprint[0] == '\0') {
		pjdlog_debug(1, "No fingerprint verification requested.");
		return;
	}

	cert = SSL_get_peer_certificate(ssl);
	if (cert == NULL)
		pjdlog_exitx(EX_TEMPFAIL, "No peer certificate received.");

	if (X509_digest(cert, EVP_sha256(), md, &mdsize) != 1)
		pjdlog_exitx(EX_TEMPFAIL, "X509_digest() failed.");
	PJDLOG_ASSERT(mdsize <= EVP_MAX_MD_SIZE);

	X509_free(cert);

	(void)strlcpy(mdstr, "SHA256=", sizeof(mdstr));
	mdstrp = mdstr + strlen(mdstr);
	for (i = 0; i < mdsize; i++) {
		PJDLOG_VERIFY(mdstrp + 3 <= mdstr + sizeof(mdstr));
		(void)sprintf(mdstrp, "%02hhX:", md[i]);
		mdstrp += 3;
	}
	/* Clear last colon. */
	mdstrp[-1] = '\0';
	if (strcasecmp(mdstr, fingerprint) != 0) {
		pjdlog_exitx(EX_NOPERM,
		    "Finger print doesn't match. Received \"%s\", expected \"%s\"",
		    mdstr, fingerprint);
	}
}
Пример #5
0
void
hastd_secondary(struct hast_resource *res, struct nv *nvin)
{
	sigset_t mask;
	pthread_t td;
	pid_t pid;
	int error, mode, debuglevel;

	/*
	 * Create communication channel between parent and child.
	 */
	if (proto_client(NULL, "socketpair://", &res->hr_ctrl) < 0) {
		KEEP_ERRNO((void)pidfile_remove(pfh));
		pjdlog_exit(EX_OSERR,
		    "Unable to create control sockets between parent and child");
	}
	/*
	 * Create communication channel between child and parent.
	 */
	if (proto_client(NULL, "socketpair://", &res->hr_event) < 0) {
		KEEP_ERRNO((void)pidfile_remove(pfh));
		pjdlog_exit(EX_OSERR,
		    "Unable to create event sockets between child and parent");
	}

	pid = fork();
	if (pid < 0) {
		KEEP_ERRNO((void)pidfile_remove(pfh));
		pjdlog_exit(EX_OSERR, "Unable to fork");
	}

	if (pid > 0) {
		/* This is parent. */
		proto_close(res->hr_remotein);
		res->hr_remotein = NULL;
		proto_close(res->hr_remoteout);
		res->hr_remoteout = NULL;
		/* Declare that we are receiver. */
		proto_recv(res->hr_event, NULL, 0);
		/* Declare that we are sender. */
		proto_send(res->hr_ctrl, NULL, 0);
		res->hr_workerpid = pid;
		return;
	}

	gres = res;
	mode = pjdlog_mode_get();
	debuglevel = pjdlog_debug_get();

	/* Declare that we are sender. */
	proto_send(res->hr_event, NULL, 0);
	/* Declare that we are receiver. */
	proto_recv(res->hr_ctrl, NULL, 0);
	descriptors_cleanup(res);

	descriptors_assert(res, mode);

	pjdlog_init(mode);
	pjdlog_debug_set(debuglevel);
	pjdlog_prefix_set("[%s] (%s) ", res->hr_name, role2str(res->hr_role));
	setproctitle("%s (%s)", res->hr_name, role2str(res->hr_role));

	PJDLOG_VERIFY(sigemptyset(&mask) == 0);
	PJDLOG_VERIFY(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);

	/* Error in setting timeout is not critical, but why should it fail? */
	if (proto_timeout(res->hr_remotein, 2 * HAST_KEEPALIVE) < 0)
		pjdlog_errno(LOG_WARNING, "Unable to set connection timeout");
	if (proto_timeout(res->hr_remoteout, res->hr_timeout) < 0)
		pjdlog_errno(LOG_WARNING, "Unable to set connection timeout");

	init_local(res);
	init_environment();

	if (drop_privs(res) != 0)
		exit(EX_CONFIG);
	pjdlog_info("Privileges successfully dropped.");

	/*
	 * Create the control thread before sending any event to the parent,
	 * as we can deadlock when parent sends control request to worker,
	 * but worker has no control thread started yet, so parent waits.
	 * In the meantime worker sends an event to the parent, but parent
	 * is unable to handle the event, because it waits for control
	 * request response.
	 */
	error = pthread_create(&td, NULL, ctrl_thread, res);
	PJDLOG_ASSERT(error == 0);

	init_remote(res, nvin);
	event_send(res, EVENT_CONNECT);

	error = pthread_create(&td, NULL, recv_thread, res);
	PJDLOG_ASSERT(error == 0);
	error = pthread_create(&td, NULL, disk_thread, res);
	PJDLOG_ASSERT(error == 0);
	(void)send_thread(res);
}
Пример #6
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);
}
Пример #7
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);
}
Пример #8
0
int
main(int argc, char **argv)
{
	char core[PATH_MAX], encryptedcore[PATH_MAX], keyfile[PATH_MAX];
	struct stat sb;
	const char *crashdir, *dumpnr, *privatekey;
	int ch, debug;
	size_t ii;
	bool usesyslog;

	pjdlog_init(PJDLOG_MODE_STD);
	pjdlog_prefix_set("(decryptcore) ");

	debug = 0;
	*core = '\0';
	crashdir = NULL;
	dumpnr = NULL;
	*encryptedcore = '\0';
	*keyfile = '\0';
	privatekey = NULL;
	usesyslog = false;
	while ((ch = getopt(argc, argv, "Lc:d:e:k:n:p:v")) != -1) {
		switch (ch) {
		case 'L':
			usesyslog = true;
			break;
		case 'c':
			strncpy(core, optarg, sizeof(core));
			break;
		case 'd':
			crashdir = optarg;
			break;
		case 'e':
			strncpy(encryptedcore, optarg, sizeof(encryptedcore));
			break;
		case 'k':
			strncpy(keyfile, optarg, sizeof(keyfile));
			break;
		case 'n':
			dumpnr = optarg;
			break;
		case 'p':
			privatekey = optarg;
			break;
		case 'v':
			debug++;
			break;
		default:
			usage();
		}
	}
	argc -= optind;
	argv += optind;

	if (argc != 0)
		usage();

	/* Verify mutually exclusive options. */
	if ((crashdir != NULL || dumpnr != NULL) &&
	    (*keyfile != '\0' || *encryptedcore != '\0' || *core != '\0')) {
		usage();
	}

	/*
	 * Set key, encryptedcore and core file names using crashdir and dumpnr.
	 */
	if (dumpnr != NULL) {
		for (ii = 0; ii < strnlen(dumpnr, PATH_MAX); ii++) {
			if (isdigit((int)dumpnr[ii]) == 0)
				usage();
		}

		if (crashdir == NULL)
			crashdir = DECRYPTCORE_CRASHDIR;
		PJDLOG_VERIFY(snprintf(keyfile, sizeof(keyfile),
		    "%s/key.%s", crashdir, dumpnr) > 0);
		PJDLOG_VERIFY(snprintf(core, sizeof(core),
		    "%s/vmcore.%s", crashdir, dumpnr) > 0);
		PJDLOG_VERIFY(snprintf(encryptedcore, sizeof(encryptedcore),
		    "%s/vmcore_encrypted.%s", crashdir, dumpnr) > 0);
	}

	if (privatekey == NULL || *keyfile == '\0' || *encryptedcore == '\0' ||
	    *core == '\0') {
		usage();
	}

	if (usesyslog)
		pjdlog_mode_set(PJDLOG_MODE_SYSLOG);
	pjdlog_debug_set(debug);

	if (!decrypt(privatekey, keyfile, encryptedcore, core)) {
		if (stat(core, &sb) == 0 && unlink(core) != 0)
			pjdlog_exit(1, "Unable to remove core");
		exit(1);
	}

	pjdlog_fini();

	exit(0);
}