X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Futil%2Fservice.c;h=0594149d96e79798697daecf13daaabf57fece8f;hb=48718834d4fb6c411ff5b00b86662a3dee3ac6cc;hp=ee1a3151fff29c92bfd5fc68610cbf65517e5f14;hpb=32fe9866f608c3ec221296b01a604210c298b54b;p=oweals%2Fgnunet.git diff --git a/src/util/service.c b/src/util/service.c index ee1a3151f..0594149d9 100644 --- a/src/util/service.c +++ b/src/util/service.c @@ -430,12 +430,8 @@ struct GNUNET_SERVICE_Context struct GNUNET_SERVER_Handle *server; /** - * Scheduler for the server. - */ - struct GNUNET_SCHEDULER_Handle *sched; - - /** - * NULL-terminated array of addresses to bind to. + * NULL-terminated array of addresses to bind to, NULL if we got pre-bound + * listen sockets. */ struct sockaddr **addrs; @@ -483,14 +479,19 @@ struct GNUNET_SERVICE_Context struct GNUNET_SERVER_MessageHandler *my_handlers; /** - * Idle timeout for server. + * Array of the lengths of the entries in addrs. */ - struct GNUNET_TIME_Relative timeout; + socklen_t * addrlens; + + /** + * NULL-terminated array of listen sockets we should take over. + */ + struct GNUNET_NETWORK_Handle **lsocks; /** - * Maximum buffer size for the server. + * Idle timeout for server. */ - size_t maxbuf; + struct GNUNET_TIME_Relative timeout; /** * Overall success/failure of the service start. @@ -511,14 +512,21 @@ struct GNUNET_SERVICE_Context int require_found; /** - * Our options. + * Do we require a matching UID for UNIX domain socket + * connections? */ - enum GNUNET_SERVICE_Options options; + int match_uid; /** - * Array of the lengths of the entries in addrs. + * Do we require a matching GID for UNIX domain socket + * connections? */ - socklen_t * addrlens; + int match_gid; + + /** + * Our options. + */ + enum GNUNET_SERVICE_Options options; }; @@ -583,9 +591,18 @@ static const struct GNUNET_SERVER_MessageHandler defhandlers[] = { /** * Check if access to the service is allowed from the given address. + * + * @param cls closure + * @param uc credentials, if available, otherwise NULL + * @param addr address + * @param addrlen length of address + * @return GNUNET_YES to allow, GNUNET_NO to deny, GNUNET_SYSERR + * for unknown address family (will be denied). */ static int -check_access (void *cls, const struct sockaddr *addr, socklen_t addrlen) +check_access (void *cls, + const struct GNUNET_CONNECTION_Credentials *uc, + const struct sockaddr *addr, socklen_t addrlen) { struct GNUNET_SERVICE_Context *sctx = cls; const struct sockaddr_in *i4; @@ -613,8 +630,23 @@ check_access (void *cls, const struct sockaddr *addr, socklen_t addrlen) (!check_ipv6_listed (sctx->v6_denied, &i6->sin6_addr))); break; case AF_UNIX: - /* FIXME: support checking UID/GID in the future... */ ret = GNUNET_OK; /* always OK for now */ + if ( (sctx->match_uid == GNUNET_YES) || + (sctx->match_gid == GNUNET_YES) ) + ret = GNUNET_NO; + if ( (uc != NULL) && + ( (sctx->match_uid != GNUNET_YES) || + (uc->uid == geteuid()) || + (uc->uid == getuid()) ) && + ( (sctx->match_gid != GNUNET_YES) || + (uc->gid == getegid()) || + (uc->gid == getgid())) ) + ret = GNUNET_YES; + else + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Access denied to UID %d / GID %d\n"), + (uc == NULL) ? -1 : uc->uid, + (uc == NULL) ? -1 : uc->gid); break; default: GNUNET_log (GNUNET_ERROR_TYPE_WARNING, @@ -732,10 +764,11 @@ add_unixpath (struct sockaddr **saddrs, unixpath, slen); un->sun_path[slen] = '\0'; - slen += sizeof (sa_family_t); #if LINUX un->sun_path[0] = '\0'; slen = sizeof (struct sockaddr_un); +#else + slen += sizeof (sa_family_t); #endif *saddrs = (struct sockaddr*) un; *saddrlens = slen; @@ -863,13 +896,23 @@ GNUNET_SERVICE_get_server_addresses (const char *serviceName, if (GNUNET_CONFIGURATION_have_value (cfg, serviceName, "UNIXPATH")) { - GNUNET_break (GNUNET_OK == + GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, serviceName, "UNIXPATH", &unixpath)); /* probe UNIX support */ + struct sockaddr_un s_un; + if (strlen(unixpath) >= sizeof(s_un.sun_path)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("UNIXPATH `%s' too long, maximum length is %llu\n"),unixpath, sizeof(s_un.sun_path)); + GNUNET_free_non_null (hostname); + GNUNET_free (unixpath); + return GNUNET_SYSERR; + } + desc = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0); if (NULL == desc) { @@ -877,6 +920,8 @@ GNUNET_SERVICE_get_server_addresses (const char *serviceName, (errno == ENOMEM) || (errno == ENFILE) || (errno == EACCES)) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket"); + GNUNET_free_non_null (hostname); + GNUNET_free (unixpath); return GNUNET_SYSERR; } GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -886,6 +931,11 @@ GNUNET_SERVICE_get_server_addresses (const char *serviceName, GNUNET_free (unixpath); unixpath = NULL; } + else + { + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc)); + desc = NULL; + } } else unixpath = NULL; @@ -899,11 +949,20 @@ GNUNET_SERVICE_get_server_addresses (const char *serviceName, GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Have neither PORT nor UNIXPATH for service `%s', but one is required\n"), serviceName); - if (desc != NULL) - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc)); GNUNET_free_non_null(hostname); return GNUNET_SYSERR; } + if (port == 0) + { + saddrs = GNUNET_malloc (2 * sizeof(struct sockaddr*)); + saddrlens = GNUNET_malloc (2 * sizeof (socklen_t)); + add_unixpath (saddrs, saddrlens, unixpath); + GNUNET_free_non_null (unixpath); + GNUNET_free_non_null(hostname); + *addrs = saddrs; + *addr_lens = saddrlens; + return 1; + } if (hostname != NULL) { @@ -923,6 +982,7 @@ GNUNET_SERVICE_get_server_addresses (const char *serviceName, _("Failed to resolve `%s': %s\n"), hostname, gai_strerror (ret)); GNUNET_free (hostname); + GNUNET_free (unixpath); return GNUNET_SYSERR; } next = res; @@ -941,6 +1001,7 @@ GNUNET_SERVICE_get_server_addresses (const char *serviceName, disablev6 ? "IPv4 " : "", hostname); freeaddrinfo (res); GNUNET_free (hostname); + GNUNET_free (unixpath); return GNUNET_SYSERR; } resi = i; @@ -1048,21 +1109,18 @@ GNUNET_SERVICE_get_server_addresses (const char *serviceName, GNUNET_free_non_null (unixpath); *addrs = saddrs; *addr_lens = saddrlens; - if (desc != NULL) - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc)); return resi; } /** - * Setup addr, addrlen, maxbuf, idle_timeout + * Setup addr, addrlen, idle_timeout * based on configuration! * * Configuration may specify: * - PORT (where to bind to for TCP) * - UNIXPATH (where to bind to for UNIX domain sockets) * - TIMEOUT (after how many ms does an inactive service timeout); - * - MAXBUF (maximum incoming message size supported) * - DISABLEV6 (disable support for IPv6, otherwise we use dual-stack) * - BINDTO (hostname or IP address to bind to, otherwise we take everything) * - ACCEPT_FROM (only allow connections from specified IPv4 subnets) @@ -1075,9 +1133,15 @@ GNUNET_SERVICE_get_server_addresses (const char *serviceName, static int setup_service (struct GNUNET_SERVICE_Context *sctx) { - unsigned long long maxbuf; struct GNUNET_TIME_Relative idleout; int tolerant; +#ifndef MINGW + const char *lpid; + unsigned int pid; + const char *nfds; + unsigned int cnt; + int flags; +#endif if (GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->serviceName, "TIMEOUT")) @@ -1097,23 +1161,6 @@ setup_service (struct GNUNET_SERVICE_Context *sctx) } else sctx->timeout = GNUNET_TIME_UNIT_FOREVER_REL; - if (GNUNET_CONFIGURATION_have_value (sctx->cfg, - sctx->serviceName, "MAXBUF")) - { - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (sctx->cfg, - sctx->serviceName, - "MAXBUF", &maxbuf)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Specified value for `%s' of service `%s' is invalid\n"), - "MAXBUF", - sctx->serviceName); - return GNUNET_SYSERR; - } - } - else - maxbuf = GNUNET_SERVER_MAX_MESSAGE_SIZE; if (GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->serviceName, "TOLERANT")) @@ -1133,23 +1180,55 @@ setup_service (struct GNUNET_SERVICE_Context *sctx) else tolerant = GNUNET_NO; - if (GNUNET_SYSERR == - GNUNET_SERVICE_get_server_addresses (sctx->serviceName, - sctx->cfg, - &sctx->addrs, - &sctx->addrlens)) - return GNUNET_SYSERR; - sctx->require_found = tolerant ? GNUNET_NO : GNUNET_YES; - sctx->maxbuf = (size_t) maxbuf; - if (sctx->maxbuf != maxbuf) +#ifndef MINGW + errno = 0; + if ( (NULL != (lpid = getenv ("LISTEN_PID"))) && + (1 == sscanf (lpid, "%u", &pid)) && + (getpid () == (pid_t) pid) && + (NULL != (nfds = getenv ("LISTEN_FDS"))) && + (1 == sscanf (nfds, "%u", &cnt)) && + (cnt > 0) && + (cnt < FD_SETSIZE) && + (cnt + 4 < FD_SETSIZE) ) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ - ("Value in configuration for `%s' and service `%s' too large!\n"), - "MAXBUF", sctx->serviceName); - return GNUNET_SYSERR; + sctx->lsocks = GNUNET_malloc (sizeof(struct GNUNET_NETWORK_Handle*) * (cnt+1)); + while (0 < cnt--) + { + flags = fcntl (3 + cnt, F_GETFD); + if ( (flags < 0) || + (0 != (flags & FD_CLOEXEC)) || + (NULL == (sctx->lsocks[cnt] = GNUNET_NETWORK_socket_box_native (3 + cnt))) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Could not access pre-bound socket %u, will try to bind myself\n"), + (unsigned int) 3 +cnt); + cnt++; + while (sctx->lsocks[cnt] != NULL) + GNUNET_break (0 == GNUNET_NETWORK_socket_close (sctx->lsocks[cnt++])); + GNUNET_free (sctx->lsocks); + sctx->lsocks = NULL; + break; + } + } + unsetenv ("LISTEN_PID"); + unsetenv ("LISTEN_FDS"); } +#endif + if ( (sctx->lsocks == NULL) && + (GNUNET_SYSERR == + GNUNET_SERVICE_get_server_addresses (sctx->serviceName, + sctx->cfg, + &sctx->addrs, + &sctx->addrlens)) ) + return GNUNET_SYSERR; + sctx->require_found = tolerant ? GNUNET_NO : GNUNET_YES; + sctx->match_uid = GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, + sctx->serviceName, + "UNIX_MATCH_UID"); + sctx->match_gid = GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, + sctx->serviceName, + "UNIX_MATCH_GID"); process_acl4 (&sctx->v4_denied, sctx, "REJECT_FROM"); process_acl4 (&sctx->v4_allowed, sctx, "ACCEPT_FROM"); process_acl6 (&sctx->v6_denied, sctx, "REJECT_FROM6"); @@ -1257,24 +1336,30 @@ service_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) struct GNUNET_SERVICE_Context *sctx = cls; unsigned int i; - sctx->sched = tc->sched; - sctx->server = GNUNET_SERVER_create (tc->sched, - &check_access, - sctx, - sctx->addrs, - sctx->addrlens, - sctx->maxbuf, - sctx->timeout, sctx->require_found); + if (sctx->lsocks != NULL) + sctx->server = GNUNET_SERVER_create_with_sockets (&check_access, + sctx, + sctx->lsocks, + sctx->timeout, sctx->require_found); + else + sctx->server = GNUNET_SERVER_create (&check_access, + sctx, + sctx->addrs, + sctx->addrlens, + sctx->timeout, sctx->require_found); if (sctx->server == NULL) { - i = 0; - while (sctx->addrs[i] != NULL) + if (sctx->addrs != NULL) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Failed to start `%s' at `%s'\n"), - sctx->serviceName, - GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i])); - i++; + i = 0; + while (sctx->addrs[i] != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Failed to start `%s' at `%s'\n"), + sctx->serviceName, + GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i])); + i++; + } } sctx->ret = GNUNET_SYSERR; return; @@ -1283,8 +1368,7 @@ service_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { /* install a task that will kill the server process if the scheduler ever gets a shutdown signal */ - GNUNET_SCHEDULER_add_delayed (tc->sched, - GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task, sctx->server); } sctx->my_handlers = GNUNET_malloc (sizeof (defhandlers)); @@ -1300,16 +1384,19 @@ service_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) sctx->ready_confirm_fd = -1; write_pid_file (sctx, getpid ()); } - i = 0; - while (sctx->addrs[i] != NULL) + if (sctx->addrs != NULL) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Service `%s' runs at %s\n"), - sctx->serviceName, - GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i])); - i++; + i = 0; + while (sctx->addrs[i] != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Service `%s' runs at %s\n"), + sctx->serviceName, + GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i])); + i++; + } } - sctx->task (sctx->task_cls, tc->sched, sctx->server, sctx->cfg); + sctx->task (sctx->task_cls, sctx->server, sctx->cfg); } @@ -1373,10 +1460,12 @@ detach_terminal (struct GNUNET_SERVICE_Context *sctx) return GNUNET_SYSERR; /* set stdin/stdout to /dev/null */ if ((dup2 (nullfd, 0) < 0) || (dup2 (nullfd, 1) < 0)) - { + { GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2"); + (void) CLOSE (nullfd); return GNUNET_SYSERR; } + (void) CLOSE (nullfd); /* Detach from controlling terminal */ pid = setsid (); if (pid == -1) @@ -1502,8 +1591,8 @@ GNUNET_SERVICE_run (int argc, sctx.ready_confirm_fd = -1; sctx.ret = GNUNET_OK; sctx.timeout = GNUNET_TIME_UNIT_FOREVER_REL; - sctx.maxbuf = GNUNET_SERVER_MAX_MESSAGE_SIZE; sctx.task = task; + sctx.task_cls = task_cls; sctx.serviceName = serviceName; sctx.cfg = cfg = GNUNET_CONFIGURATION_create (); /* setup subsystems */ @@ -1565,13 +1654,11 @@ shutdown: * initialized system. * * @param serviceName our service name - * @param sched scheduler to use * @param cfg configuration to use * @return NULL on error, service handle */ struct GNUNET_SERVICE_Context * GNUNET_SERVICE_start (const char *serviceName, - struct GNUNET_SCHEDULER_Handle *sched, const struct GNUNET_CONFIGURATION_Handle *cfg) { int i; @@ -1581,21 +1668,29 @@ GNUNET_SERVICE_start (const char *serviceName, sctx->ready_confirm_fd = -1; /* no daemonizing */ sctx->ret = GNUNET_OK; sctx->timeout = GNUNET_TIME_UNIT_FOREVER_REL; - sctx->maxbuf = GNUNET_SERVER_MAX_MESSAGE_SIZE; sctx->serviceName = serviceName; sctx->cfg = cfg; - sctx->sched = sched; /* setup subsystems */ - if ((GNUNET_OK != setup_service (sctx)) || - (NULL == (sctx->server = GNUNET_SERVER_create (sched, - &check_access, - sctx, - sctx->addrs, - sctx->addrlens, - sctx->maxbuf, - sctx->timeout, - sctx->require_found)))) + if (GNUNET_OK != setup_service (sctx)) + { + GNUNET_SERVICE_stop (sctx); + return NULL; + } + if (sctx->lsocks != NULL) + sctx->server = GNUNET_SERVER_create_with_sockets (&check_access, + sctx, + sctx->lsocks, + sctx->timeout, sctx->require_found); + else + sctx->server = GNUNET_SERVER_create (&check_access, + sctx, + sctx->addrs, + sctx->addrlens, + sctx->timeout, + sctx->require_found); + + if (NULL == sctx->server) { GNUNET_SERVICE_stop (sctx); return NULL; @@ -1635,10 +1730,13 @@ GNUNET_SERVICE_stop (struct GNUNET_SERVICE_Context *sctx) if (NULL != sctx->server) GNUNET_SERVER_destroy (sctx->server); GNUNET_free_non_null (sctx->my_handlers); - i = 0; - while (sctx->addrs[i] != NULL) - GNUNET_free (sctx->addrs[i++]); - GNUNET_free_non_null (sctx->addrs); + if (sctx->addrs != NULL) + { + i = 0; + while (sctx->addrs[i] != NULL) + GNUNET_free (sctx->addrs[i++]); + GNUNET_free (sctx->addrs); + } GNUNET_free_non_null (sctx->addrlens); GNUNET_free_non_null (sctx->v4_denied); GNUNET_free_non_null (sctx->v6_denied);