2 * Copyright (c) 2012 The FreeBSD Foundation
5 * This software was developed by Pawel Jakub Dawidek under sponsorship from
6 * the FreeBSD Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/types.h>
34 #include <sys/capsicum.h>
35 #include <sys/queue.h>
36 #include <sys/socket.h>
54 #include <libcapsicum.h>
55 #include <libcapsicum_impl.h>
56 #include <libcasper.h>
57 #include <libcasper_impl.h>
66 #define CASPERD_PIDFILE "/var/run/casperd.pid"
67 #define CASPERD_SERVCONFDIR "/etc/casper"
68 #define CASPERD_SOCKPATH "/var/run/casper"
70 typedef void service_function_t(struct service_connection *, const nvlist_t *,
73 struct casper_service {
75 const char *cs_execpath;
76 struct service *cs_service;
78 TAILQ_ENTRY(casper_service) cs_next;
81 static TAILQ_HEAD(, casper_service) casper_services =
82 TAILQ_HEAD_INITIALIZER(casper_services);
84 #define SERVICE_IS_CORE(service) ((service)->cs_execpath == NULL)
86 static void service_external_execute(int chanfd);
88 #define KEEP_ERRNO(work) do { \
96 static struct casper_service *
97 service_find(const char *name)
99 struct casper_service *casserv;
101 TAILQ_FOREACH(casserv, &casper_services, cs_next) {
102 if (strcmp(casserv->cs_name, name) == 0)
109 * Function always consumes the given attrs.
112 service_register(nvlist_t *attrs)
114 struct casper_service *casserv;
117 PJDLOG_ASSERT(nvlist_exists_string(attrs, "name"));
118 PJDLOG_ASSERT(nvlist_exists_string(attrs, "execpath") ||
119 (nvlist_exists_number(attrs, "commandfunc") &&
120 nvlist_exists_number(attrs, "limitfunc")));
122 name = nvlist_get_string(attrs, "name");
123 PJDLOG_ASSERT(name != NULL);
124 if (name[0] == '\0') {
125 pjdlog_error("Unable to register service with an empty name.");
126 nvlist_destroy(attrs);
129 if (service_find(name) != NULL) {
130 pjdlog_error("Service \"%s\" is already registered.", name);
131 nvlist_destroy(attrs);
135 casserv = malloc(sizeof(*casserv));
136 if (casserv == NULL) {
137 pjdlog_errno(LOG_ERR, "Unable to register service \"%s\"",
139 nvlist_destroy(attrs);
142 casserv->cs_name = name;
143 if (nvlist_exists_string(attrs, "execpath")) {
146 PJDLOG_ASSERT(!nvlist_exists_number(attrs, "commandfunc"));
147 PJDLOG_ASSERT(!nvlist_exists_number(attrs, "limitfunc"));
149 casserv->cs_service = NULL;
151 casserv->cs_execpath = nvlist_get_string(attrs, "execpath");
152 if (casserv->cs_execpath == NULL ||
153 casserv->cs_execpath[0] == '\0') {
154 pjdlog_error("Unable to register service with an empty execpath.");
156 nvlist_destroy(attrs);
159 if (stat(casserv->cs_execpath, &sb) == -1) {
160 pjdlog_errno(LOG_ERR,
161 "Unable to register service \"%s\", problem with executable \"%s\"",
162 name, casserv->cs_execpath);
164 nvlist_destroy(attrs);
167 } else /* if (nvlist_exists_number(attrs, "commandfunc")) */ {
168 PJDLOG_ASSERT(!nvlist_exists_string(attrs, "execpath"));
170 casserv->cs_execpath = NULL;
172 casserv->cs_service = service_alloc(name,
173 (void *)(uintptr_t)nvlist_get_number(attrs, "limitfunc"),
174 (void *)(uintptr_t)nvlist_get_number(attrs, "commandfunc"));
175 if (casserv->cs_service == NULL) {
176 pjdlog_errno(LOG_ERR,
177 "Unable to register service \"%s\"", name);
179 nvlist_destroy(attrs);
183 casserv->cs_attrs = attrs;
184 TAILQ_INSERT_TAIL(&casper_services, casserv, cs_next);
185 pjdlog_debug(1, "Service %s successfully registered.",
190 casper_allowed_service(const nvlist_t *limits, const char *service)
196 if (nvlist_exists_null(limits, service))
203 casper_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
210 while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
211 if (type != NV_TYPE_NULL)
213 if (!casper_allowed_service(oldlimits, name))
214 return (ENOTCAPABLE);
221 casper_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
224 struct casper_service *casserv;
225 const char *servname;
227 int chanfd, execfd, procfd, error;
229 if (strcmp(cmd, "open") != 0)
231 if (!nvlist_exists_string(nvlin, "service"))
234 servname = nvlist_get_string(nvlin, "service");
236 casserv = service_find(servname);
240 if (!casper_allowed_service(limits, servname))
241 return (ENOTCAPABLE);
243 #ifdef O_EXEC_WORKING
244 execfd = open(casserv->cs_execpath, O_EXEC);
246 execfd = open(casserv->cs_execpath, O_RDONLY);
250 pjdlog_errno(LOG_ERR,
251 "Unable to open executable '%s' of service '%s'",
252 casserv->cs_execpath, casserv->cs_name);
256 if (zygote_clone(service_external_execute, 0, &chanfd, &procfd) == -1) {
262 nvl = nvlist_create(0);
263 nvlist_add_string(nvl, "service", casserv->cs_name);
264 if (nvlist_exists_descriptor(nvlin, "stderrfd")) {
265 nvlist_move_descriptor(nvl, "stderrfd",
266 nvlist_take_descriptor(nvlin, "stderrfd"));
268 nvlist_move_descriptor(nvl, "execfd", execfd);
269 nvlist_move_descriptor(nvl, "procfd", procfd);
270 if (nvlist_send(chanfd, nvl) == -1) {
272 pjdlog_errno(LOG_ERR, "Unable to send nvlist");
279 nvlist_move_descriptor(nvlout, "chanfd", chanfd);
285 fdswap(int *fd0, int *fd1)
289 PJDLOG_VERIFY((tmpfd = dup(*fd0)) != -1);
290 PJDLOG_VERIFY(dup2(*fd1, *fd0) != -1);
291 PJDLOG_VERIFY(dup2(tmpfd, *fd1) != -1);
299 fdmove(int *oldfdp, int newfd)
302 if (*oldfdp != newfd) {
303 PJDLOG_VERIFY(dup2(*oldfdp, newfd) != -1);
314 flags = fcntl(fd, F_GETFD);
315 PJDLOG_ASSERT(flags != -1);
316 if ((flags & FD_CLOEXEC) != 0)
317 PJDLOG_VERIFY(fcntl(fd, F_SETFD, flags & ~FD_CLOEXEC) != -1);
321 service_register_core(void)
325 nvl = nvlist_create(0);
326 nvlist_add_string(nvl, "name", "core.casper");
327 nvlist_add_number(nvl, "limitfunc", (uint64_t)(uintptr_t)casper_limit);
328 nvlist_add_number(nvl, "commandfunc",
329 (uint64_t)(uintptr_t)casper_command);
330 service_register(nvl);
334 setup_creds(int sock)
336 struct cmsgcred cred;
338 if (cred_recv(sock, &cred) == -1)
341 if (setgroups((int)cred.cmcred_ngroups, cred.cmcred_groups) == -1)
344 if (setgid(cred.cmcred_groups[0]) == -1)
347 if (setuid(cred.cmcred_euid) == -1)
354 service_external_execute(int chanfd)
356 char *service, *argv[3];
357 int stderrfd, execfd, procfd;
360 nvl = nvlist_recv(chanfd);
362 pjdlog_exit(1, "Unable to receive nvlist");
363 service = nvlist_take_string(nvl, "service");
364 PJDLOG_ASSERT(service != NULL);
365 if (nvlist_exists_descriptor(nvl, "stderrfd")) {
366 stderrfd = nvlist_take_descriptor(nvl, "stderrfd");
368 stderrfd = open(_PATH_DEVNULL, O_RDWR);
370 pjdlog_exit(1, "Unable to open %s", _PATH_DEVNULL);
372 execfd = nvlist_take_descriptor(nvl, "execfd");
373 procfd = nvlist_take_descriptor(nvl, "procfd");
377 * Move all descriptors into right slots.
380 if (stderrfd != STDERR_FILENO) {
381 if (chanfd == STDERR_FILENO)
382 fdswap(&stderrfd, &chanfd);
383 else if (execfd == STDERR_FILENO)
384 fdswap(&stderrfd, &execfd);
385 else if (procfd == STDERR_FILENO)
386 fdswap(&stderrfd, &procfd);
387 fdmove(&stderrfd, STDERR_FILENO);
391 if (chanfd != PARENT_FILENO) {
392 if (execfd == PARENT_FILENO)
393 fdswap(&chanfd, &execfd);
394 else if (procfd == PARENT_FILENO)
395 fdswap(&chanfd, &procfd);
396 fdmove(&chanfd, PARENT_FILENO);
400 if (execfd != EXECUTABLE_FILENO) {
401 if (procfd == EXECUTABLE_FILENO)
402 fdswap(&execfd, &procfd);
403 fdmove(&execfd, EXECUTABLE_FILENO);
407 if (procfd != PROC_FILENO)
408 fdmove(&procfd, PROC_FILENO);
412 * Use credentials of the caller process.
417 asprintf(&argv[1], "%d", pjdlog_debug_get());
420 fexecve(execfd, argv, NULL);
421 pjdlog_exit(1, "Unable to execute service %s", service);
425 service_register_external_one(const char *dirpath, int dfd,
426 const char *filename)
428 char execpath[FILENAME_MAX];
433 fd = openat(dfd, filename, O_RDONLY);
435 pjdlog_errno(LOG_ERR, "Unable to open \"%s/%s\"", dirpath,
440 done = read(fd, execpath, sizeof(execpath));
442 pjdlog_errno(LOG_ERR, "Unable to read content of \"%s/%s\"",
448 if (done == sizeof(execpath)) {
449 pjdlog_error("Executable path too long in \"%s/%s\".", dirpath,
453 execpath[done] = '\0';
455 if (execpath[--done] == '\n')
456 execpath[done] = '\0';
459 nvl = nvlist_create(0);
460 nvlist_add_string(nvl, "name", filename);
461 nvlist_add_string(nvl, "execpath", execpath);
462 if (nvlist_error(nvl) != 0) {
463 pjdlog_common(LOG_ERR, 0, nvlist_error(nvl),
464 "Unable to allocate attributes for service \"%s/%s\"",
470 service_register(nvl);
471 /* service_register() consumed nvl. */
475 file_type(int dfd, const char *filename)
479 if (fstatat(dfd, filename, &sb, AT_SYMLINK_NOFOLLOW) == -1) {
480 pjdlog_errno(LOG_ERR, "Unable to stat \"%s\"", filename);
483 return (IFTODT(sb.st_mode));
487 service_register_external(const char *dirpath)
493 dirp = opendir(dirpath);
495 pjdlog_errno(LOG_WARNING, "Unable to open \"%s\"", dirpath);
499 PJDLOG_ASSERT(dfd >= 0);
500 while ((dp = readdir(dirp)) != NULL) {
501 dp->d_type = file_type(dfd, dp->d_name);
502 /* We are only interested in regular files, skip the rest. */
503 if (dp->d_type != DT_REG) {
505 "File \"%s/%s\" is not a regular file, skipping.",
506 dirpath, dp->d_name);
509 service_register_external_one(dirpath, dfd, dp->d_name);
515 casper_accept(int lsock)
517 struct casper_service *casserv;
518 struct service_connection *sconn;
521 sock = accept(lsock, NULL, NULL);
523 pjdlog_errno(LOG_ERR, "Unable to accept casper connection");
526 casserv = service_find("core.casper");
527 PJDLOG_ASSERT(casserv != NULL);
529 sconn = service_connection_add(casserv->cs_service, sock, NULL);
537 main_loop(const char *sockpath, struct pidfh *pfh)
540 struct sockaddr_un sun;
541 struct casper_service *casserv;
542 struct service_connection *sconn, *sconntmp;
543 int lsock, sock, maxfd, ret;
546 lsock = socket(AF_UNIX, SOCK_STREAM, 0);
548 pjdlog_exit(1, "Unable to create socket");
550 (void)unlink(sockpath);
552 bzero(&sun, sizeof(sun));
553 sun.sun_family = AF_UNIX;
554 PJDLOG_VERIFY(strlcpy(sun.sun_path, sockpath, sizeof(sun.sun_path)) <
555 sizeof(sun.sun_path));
556 sun.sun_len = SUN_LEN(&sun);
558 oldumask = umask(S_IXUSR | S_IXGRP | S_IXOTH);
559 if (bind(lsock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
560 pjdlog_exit(1, "Unable to bind to %s", sockpath);
561 (void)umask(oldumask);
562 if (listen(lsock, 8) == -1)
563 pjdlog_exit(1, "Unable to listen on %s", sockpath);
569 TAILQ_FOREACH(casserv, &casper_services, cs_next) {
570 /* We handle only core services. */
571 if (!SERVICE_IS_CORE(casserv))
573 for (sconn = service_connection_first(casserv->cs_service);
575 sconn = service_connection_next(sconn)) {
576 sock = service_connection_get_sock(sconn);
578 maxfd = sock > maxfd ? sock : maxfd;
583 PJDLOG_ASSERT(maxfd <= (int)FD_SETSIZE);
584 ret = select(maxfd, &fds, NULL, NULL, NULL);
585 PJDLOG_ASSERT(ret == -1 || ret > 0); /* select() cannot timeout */
589 KEEP_ERRNO((void)pidfile_remove(pfh));
590 pjdlog_exit(1, "select() failed");
593 if (FD_ISSET(lsock, &fds))
594 casper_accept(lsock);
595 TAILQ_FOREACH(casserv, &casper_services, cs_next) {
596 /* We handle only core services. */
597 if (!SERVICE_IS_CORE(casserv))
599 for (sconn = service_connection_first(casserv->cs_service);
600 sconn != NULL; sconn = sconntmp) {
602 * Prepare for connection to be removed from
603 * the list on failure.
605 sconntmp = service_connection_next(sconn);
606 sock = service_connection_get_sock(sconn);
607 if (FD_ISSET(sock, &fds)) {
608 service_message(casserv->cs_service,
621 "usage: casperd [-Fhv] [-D servconfdir] [-P pidfile] [-S sockpath]");
625 main(int argc, char *argv[])
628 const char *pidfile, *servconfdir, *sockpath;
633 pjdlog_init(PJDLOG_MODE_STD);
637 pidfile = CASPERD_PIDFILE;
638 servconfdir = CASPERD_SERVCONFDIR;
639 sockpath = CASPERD_SOCKPATH;
641 while ((ch = getopt(argc, argv, "D:FhP:S:v")) != -1) {
644 servconfdir = optarg;
670 pjdlog_mode_set(PJDLOG_MODE_SYSLOG);
671 pjdlog_prefix_set("(casperd) ");
672 pjdlog_debug_set(debug);
674 if (zygote_init() < 0)
675 pjdlog_exit(1, "Unable to create zygote process");
677 pfh = pidfile_open(pidfile, 0600, &otherpid);
679 if (errno == EEXIST) {
680 pjdlog_exitx(1, "casperd already running, pid: %jd.",
683 pjdlog_errno(LOG_WARNING, "Cannot open or create pidfile %s",
688 if (daemon(0, 0) == -1) {
689 KEEP_ERRNO((void)pidfile_remove(pfh));
690 pjdlog_exit(1, "Unable to go into background");
694 /* Write PID to a file. */
695 if (pidfile_write(pfh) == -1) {
696 pjdlog_errno(LOG_WARNING, "Unable to write to pidfile %s",
699 pjdlog_debug(1, "PID stored in %s.", pidfile);
703 * Register core services.
705 service_register_core();
707 * Register external services.
709 service_register_external(servconfdir);
712 * Wait for connections.
714 main_loop(sockpath, pfh);