diff options
Diffstat (limited to 'frontend/saned.c')
-rw-r--r-- | frontend/saned.c | 533 |
1 files changed, 319 insertions, 214 deletions
diff --git a/frontend/saned.c b/frontend/saned.c index 3bb99bb..6c3c40d 100644 --- a/frontend/saned.c +++ b/frontend/saned.c @@ -87,7 +87,7 @@ #if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL) # include <sys/poll.h> #else -/* +/* * This replacement poll() using select() is only designed to cover * our needs in run_standalone(). It should probably be extended... */ @@ -203,13 +203,13 @@ static AvahiEntryGroup *avahi_group = NULL; (((const uint32_t *) (a))[0] == 0 \ && ((const uint32_t *) (a))[1] == 0 \ && ((const uint32_t *) (a))[2] == 0 \ - && ((const uint32_t *) (a))[3] == htonl (1)) + && ((const uint32_t *) (a))[3] == htonl (1)) # endif # ifndef IN6_IS_ADDR_V4MAPPED # define IN6_IS_ADDR_V4MAPPED(a) \ ((((const uint32_t *) (a))[0] == 0) \ && (((const uint32_t *) (a))[1] == 0) \ - && (((const uint32_t *) (a))[2] == htonl (0xffff))) + && (((const uint32_t *) (a))[2] == htonl (0xffff))) # endif #endif /* ENABLE_IPV6 */ @@ -251,6 +251,9 @@ static Wire wire; static int num_handles; static int debug; static int run_mode; +static int run_foreground; +static int run_once; +static int data_connect_timeout = 4000; static Handle *handle; static char *bind_addr; static union @@ -298,9 +301,7 @@ static SANE_Bool log_to_syslog = SANE_TRUE; static int process_request (Wire * w); #define SANED_RUN_INETD 0 -#define SANED_RUN_DEBUG 1 -#define SANED_RUN_ALONE 2 - +#define SANED_RUN_ALONE 1 #define DBG_ERR 1 #define DBG_WARN 2 @@ -402,7 +403,7 @@ auth_callback (SANE_String_Const res, break; default: - DBG (DBG_WARN, + DBG (DBG_WARN, "auth_callback: called for unexpected request %d (resource=%s)\n", current_request, res); break; @@ -430,7 +431,7 @@ auth_callback (SANE_String_Const res, { DBG (DBG_WARN, "auth_callback: bad procedure number %d " - "(expected: %d, resource=%s)\n", procnum, SANE_NET_AUTHORIZE, + "(expected: %d, resource=%s)\n", procnum, SANE_NET_AUTHORIZE, res); return; } @@ -565,7 +566,7 @@ check_v4_in_range (struct sockaddr_in *sin, char *base_ip, char *netmask) int cidr; int i, err; char *end; - uint32_t mask; + uint32_t mask; struct sockaddr_in *base; struct addrinfo hints; struct addrinfo *res; @@ -573,7 +574,7 @@ check_v4_in_range (struct sockaddr_in *sin, char *base_ip, char *netmask) cidr = -1; cidr = strtol (netmask, &end, 10); - + /* Sanity check on the cidr value */ if ((cidr < 0) || (cidr > 32) || (end == netmask)) { @@ -584,13 +585,13 @@ check_v4_in_range (struct sockaddr_in *sin, char *base_ip, char *netmask) mask = 0; cidr -= 8; - /* Build a bitmask out of the CIDR value */ + /* Build a bitmask out of the CIDR value */ for (i = 3; cidr >= 0; i--) { mask |= (0xff << (8 * i)); cidr -= 8; } - + if (cidr < 0) mask |= (cidrtomask[cidr + 8] << (8 * i)); @@ -600,7 +601,7 @@ check_v4_in_range (struct sockaddr_in *sin, char *base_ip, char *netmask) memset (&hints, 0, sizeof (struct addrinfo)); hints.ai_flags = AI_NUMERICHOST; hints.ai_family = PF_INET; - + err = getaddrinfo (base_ip, NULL, &hints, &res); if (err) { @@ -616,9 +617,9 @@ check_v4_in_range (struct sockaddr_in *sin, char *base_ip, char *netmask) */ if ((base->sin_addr.s_addr & mask) == (sin->sin_addr.s_addr & mask)) ret = SANE_TRUE; - + freeaddrinfo (res); - + return ret; } @@ -649,17 +650,17 @@ check_v6_in_range (struct sockaddr_in6 *sin6, char *base_ip, char *netmask) memset (mask, 0, (16 * sizeof (unsigned int))); cidr -= 8; - + /* Build a bitmask out of the CIDR value */ for (i = 0; cidr >= 0; i++) { mask[i] = 0xff; cidr -= 8; } - + if (cidr < 0) mask[i] = cidrtomask[cidr + 8]; - + /* get a sockaddr_in6 representing the base IP address */ memset (&hints, 0, sizeof (struct addrinfo)); hints.ai_flags = AI_NUMERICHOST; @@ -686,9 +687,9 @@ check_v6_in_range (struct sockaddr_in6 *sin6, char *base_ip, char *netmask) break; } } - + freeaddrinfo (res); - + return ret; } # endif /* ENABLE_IPV6 */ @@ -699,12 +700,12 @@ check_v4_in_range (struct in_addr *inaddr, struct in_addr *base, char *netmask) int cidr; int i; char *end; - uint32_t mask; + uint32_t mask; SANE_Bool ret = SANE_FALSE; cidr = -1; cidr = strtol (netmask, &end, 10); - + /* sanity check on the cidr value */ if ((cidr < 0) || (cidr > 32) || (end == netmask)) { @@ -714,14 +715,14 @@ check_v4_in_range (struct in_addr *inaddr, struct in_addr *base, char *netmask) mask = 0; cidr -= 8; - + /* Build a bitmask out of the CIDR value */ for (i = 3; cidr >= 0; i--) { mask |= (0xff << (8 * i)); cidr -= 8; } - + if (cidr < 0) mask |= (cidrtomask[cidr + 8] << (8 * i)); @@ -733,7 +734,7 @@ check_v4_in_range (struct in_addr *inaddr, struct in_addr *base, char *netmask) */ if ((base->s_addr & mask) == (inaddr->s_addr & mask)) ret = SANE_TRUE; - + return ret; } #endif /* SANED_USES_AF_INDEP */ @@ -801,7 +802,7 @@ check_host (int fd) memset (&hints, 0, sizeof (struct addrinfo)); hints.ai_flags = AI_NUMERICHOST; hints.ai_family = PF_INET; - + err = getaddrinfo (remote_ipv4, NULL, &hints, &res); if (err) { @@ -901,13 +902,13 @@ check_host (int fd) { DBG (DBG_DBG, "check_host: local hostname(s) (from DNS): %s\n", resp->ai_canonname); - + err = getnameinfo (resp->ai_addr, resp->ai_addrlen, text_addr, sizeof (text_addr), NULL, 0, NI_NUMERICHOST); if (err) strncpy (text_addr, "[error]", 8); -#ifdef ENABLE_IPV6 +#ifdef ENABLE_IPV6 if ((strcasecmp (text_addr, remote_ip) == 0) || ((IPv4map == SANE_TRUE) && (strcmp (text_addr, remote_ipv4) == 0))) #else @@ -915,18 +916,18 @@ check_host (int fd) #endif /* ENABLE_IPV6 */ { DBG (DBG_MSG, "check_host: remote host has same addr as local: access granted\n"); - + freeaddrinfo (res); res = NULL; return SANE_STATUS_GOOD; } } - + freeaddrinfo (res); res = NULL; - - DBG (DBG_DBG, + + DBG (DBG_DBG, "check_host: remote host doesn't have same addr as local\n"); } @@ -947,8 +948,8 @@ check_host (int fd) config_file_names[j], strerror (errno)); continue; } - - while (!access_ok && sanei_config_read (config_line_buf, + + while (!access_ok && sanei_config_read (config_line_buf, sizeof (config_line_buf), fp)) { config_line = config_line_buf; /* from now on, use a pointer */ @@ -993,7 +994,7 @@ check_host (int fd) if (strcmp (config_line, "+") == 0) { access_ok = 1; - DBG (DBG_DBG, + DBG (DBG_DBG, "check_host: access granted from any host (`+')\n"); } /* compare remote_ip (remote IP address) to the config_line */ @@ -1033,7 +1034,7 @@ check_host (int fd) memset (&hints, 0, sizeof (struct addrinfo)); hints.ai_flags = AI_NUMERICHOST; hints.ai_family = PF_INET; - + err = getaddrinfo (remote_ipv4, NULL, &hints, &res); if (err) DBG (DBG_DBG, "check_host: getaddrinfo() failed: %s\n", gai_strerror (err)); @@ -1044,7 +1045,7 @@ check_host (int fd) if ((SS_FAMILY(remote_address.ss) == AF_INET) || (IPv4map == SANE_TRUE)) { - + if (check_v4_in_range (sin, config_line, netmask)) { DBG (DBG_DBG, "check_host: access granted from IP address %s (in subnet %s/%s)\n", @@ -1056,7 +1057,7 @@ check_host (int fd) /* restore the old sin pointer */ sin = &remote_address.sin; } - + if (res != NULL) { freeaddrinfo (res); @@ -1086,11 +1087,11 @@ check_host (int fd) #else hints.ai_family = PF_INET; #endif /* ENABLE_IPV6 */ - + err = getaddrinfo (config_line, NULL, &hints, &res); if (err) { - DBG (DBG_DBG, + DBG (DBG_DBG, "check_host: getaddrinfo for `%s' failed: %s\n", config_line, gai_strerror (err)); DBG (DBG_MSG, "check_host: entry isn't an IP address " @@ -1105,19 +1106,19 @@ check_host (int fd) sizeof (text_addr), NULL, 0, NI_NUMERICHOST); if (err) strncpy (text_addr, "[error]", 8); - - DBG (DBG_MSG, - "check_host: DNS lookup returns IP address: %s\n", - text_addr); - -#ifdef ENABLE_IPV6 + + DBG (DBG_MSG, + "check_host: DNS lookup returns IP address: %s\n", + text_addr); + +#ifdef ENABLE_IPV6 if ((strcasecmp (text_addr, remote_ip) == 0) || ((IPv4map == SANE_TRUE) && (strcmp (text_addr, remote_ipv4) == 0))) #else if (strcmp (text_addr, remote_ip) == 0) #endif /* ENABLE_IPV6 */ access_ok = 1; - + if (access_ok) break; } @@ -1128,10 +1129,10 @@ check_host (int fd) } fclose (fp); } - + if (access_ok) return SANE_STATUS_GOOD; - + return SANE_STATUS_ACCESS_DENIED; } @@ -1150,7 +1151,7 @@ check_host (int fd) char hostname[MAXHOSTNAMELEN]; char *r_hostname; static struct in_addr config_line_address; - + int len; FILE *fp; @@ -1164,7 +1165,7 @@ check_host (int fd) } r_hostname = inet_ntoa (sin.sin_addr); remote_ip = strdup (r_hostname); - DBG (DBG_WARN, "check_host: access by remote host: %s\n", + DBG (DBG_WARN, "check_host: access by remote host: %s\n", remote_ip); /* Save remote address for check of control and data connections */ memcpy (&remote_address, &sin.sin_addr, sizeof (remote_address)); @@ -1203,7 +1204,7 @@ check_host (int fd) { DBG (DBG_DBG, "check_host: local hostname (from DNS): %s\n", he->h_name); - + if ((he->h_length == 4) || (he->h_addrtype == AF_INET)) { if (!inet_ntop (he->h_addrtype, he->h_addr_list[0], text_addr, @@ -1211,9 +1212,9 @@ check_host (int fd) strcpy (text_addr, "[error]"); DBG (DBG_DBG, "check_host: local host address (from DNS): %s\n", text_addr); - if (memcmp (he->h_addr_list[0], &remote_address.s_addr, 4) == 0) + if (memcmp (he->h_addr_list[0], &remote_address.s_addr, 4) == 0) { - DBG (DBG_MSG, + DBG (DBG_MSG, "check_host: remote host has same addr as local: " "access accepted\n"); return SANE_STATUS_GOOD; @@ -1246,8 +1247,8 @@ check_host (int fd) config_file_names[j], strerror (errno)); continue; } - - while (!access_ok && sanei_config_read (config_line_buf, + + while (!access_ok && sanei_config_read (config_line_buf, sizeof (config_line_buf), fp)) { config_line = config_line_buf; /* from now on, use a pointer */ @@ -1275,14 +1276,14 @@ check_host (int fd) if (strcmp (config_line, "+") == 0) { access_ok = 1; - DBG (DBG_DBG, + DBG (DBG_DBG, "check_host: access accepted from any host (`+')\n"); } else { if (inet_pton (AF_INET, config_line, &config_line_address) > 0) { - if (memcmp (&remote_address.s_addr, + if (memcmp (&remote_address.s_addr, &config_line_address.s_addr, 4) == 0) access_ok = 1; else if (netmask != NULL) @@ -1297,13 +1298,13 @@ check_host (int fd) } else { - DBG (DBG_DBG, + DBG (DBG_DBG, "check_host: inet_pton for `%s' failed\n", config_line); he = gethostbyname (config_line); if (!he) { - DBG (DBG_DBG, + DBG (DBG_DBG, "check_host: gethostbyname for `%s' failed: %s\n", config_line, hstrerror (h_errno)); DBG (DBG_MSG, "check_host: entry isn't an IP address " @@ -1313,10 +1314,10 @@ check_host (int fd) if (!inet_ntop (he->h_addrtype, he->h_addr_list[0], text_addr, sizeof (text_addr))) strcpy (text_addr, "[error]"); - DBG (DBG_MSG, + DBG (DBG_MSG, "check_host: DNS lookup returns IP address: %s\n", text_addr); - if (memcmp (&remote_address.s_addr, + if (memcmp (&remote_address.s_addr, he->h_addr_list[0], 4) == 0) access_ok = 1; } @@ -1356,7 +1357,7 @@ init (Wire * w) DBG (DBG_ERR, "init: bad status after sanei_w_set_dir: %d\n", w->status); return -1; } - + sanei_w_word (w, &word); /* decode procedure number */ if (w->status || word != SANE_NET_INIT) { @@ -1658,7 +1659,7 @@ do_scan (Wire * w, int h, int data_fd) long int nwritten; SANE_Int length; size_t nbytes; - + DBG (3, "do_scan: start\n"); FD_ZERO (&rd_mask); @@ -1699,8 +1700,8 @@ do_scan (Wire * w, int h, int data_fd) FD_CLR (be_fd, &rd_mask); be_fd = -1; /* only set status_dirty if EOF hasn't been already detected */ - if (status == SANE_STATUS_GOOD) - status_dirty = 1; + if (status == SANE_STATUS_GOOD) + status_dirty = 1; status = SANE_STATUS_EOF; DBG (DBG_INFO, "do_scan: select_fd was closed --> EOF\n"); continue; @@ -1723,17 +1724,18 @@ do_scan (Wire * w, int h, int data_fd) nbytes = bytes_in_buf; if (writer + nbytes > sizeof (buf)) nbytes = sizeof (buf) - writer; - DBG (DBG_INFO, + DBG (DBG_INFO, "do_scan: trying to write %d bytes to client\n", nbytes); nwritten = write (data_fd, buf + writer, nbytes); - DBG (DBG_INFO, + DBG (DBG_INFO, "do_scan: wrote %ld bytes to client\n", nwritten); if (nwritten < 0) { DBG (DBG_ERR, "do_scan: write failed (%s)\n", strerror (errno)); status = SANE_STATUS_CANCELLED; + handle[h].docancel = 1; break; } bytes_in_buf -= nwritten; @@ -1791,7 +1793,7 @@ do_scan (Wire * w, int h, int data_fd) reader = store_reclen (buf, sizeof (buf), reader, 0xffffffff); buf[reader] = status; bytes_in_buf += 5; - DBG (DBG_MSG, "do_scan: statuscode `%s' was added to buffer\n", + DBG (DBG_MSG, "do_scan: statuscode `%s' was added to buffer\n", sane_strstatus(status)); } @@ -1799,13 +1801,19 @@ do_scan (Wire * w, int h, int data_fd) { DBG (DBG_MSG, "do_scan: processing RPC request on fd %d\n", w->io.fd); - process_request (w); + if(process_request (w) < 0) + handle[h].docancel = 1; + if (handle[h].docancel) break; } } while (status == SANE_STATUS_GOOD || bytes_in_buf > 0 || status_dirty); DBG (DBG_MSG, "do_scan: done, status=%s\n", sane_strstatus (status)); + + if(handle[h].docancel) + sane_cancel (handle[h].handle); + handle[h].docancel = 0; handle[h].scanning = 0; } @@ -1854,7 +1862,7 @@ process_request (Wire * w) sanei_w_string (w, &name); if (w->status) { - DBG (DBG_ERR, + DBG (DBG_ERR, "process_request: (open) error while decoding args (%s)\n", strerror (w->status)); return 1; @@ -1871,7 +1879,7 @@ process_request (Wire * w) can_authorize = 1; resource = strdup (name); - + if (strlen(resource) == 0) { const SANE_Device **device_list; @@ -1879,8 +1887,8 @@ process_request (Wire * w) DBG(DBG_DBG, "process_request: (open) strlen(resource) == 0\n"); free (resource); - if ((i = sane_get_devices (&device_list, SANE_TRUE)) != - SANE_STATUS_GOOD) + if ((i = sane_get_devices (&device_list, SANE_TRUE)) != + SANE_STATUS_GOOD) { DBG(DBG_ERR, "process_request: (open) sane_get_devices failed\n"); memset (&reply, 0, sizeof (reply)); @@ -1889,7 +1897,7 @@ process_request (Wire * w) break; } - if ((device_list == NULL) || (device_list[0] == NULL)) + if ((device_list == NULL) || (device_list[0] == NULL)) { DBG(DBG_ERR, "process_request: (open) device_list[0] == 0\n"); memset (&reply, 0, sizeof (reply)); @@ -1907,7 +1915,7 @@ process_request (Wire * w) if (sanei_authorize (resource, "saned", auth_callback) != SANE_STATUS_GOOD) { - DBG (DBG_ERR, "process_request: access to resource `%s' denied\n", + DBG (DBG_ERR, "process_request: access to resource `%s' denied\n", resource); free (resource); memset (&reply, 0, sizeof (reply)); /* avoid leaking bits */ @@ -1915,12 +1923,12 @@ process_request (Wire * w) } else { - DBG (DBG_MSG, "process_request: access to resource `%s' granted\n", + DBG (DBG_MSG, "process_request: access to resource `%s' granted\n", resource); free (resource); memset (&reply, 0, sizeof (reply)); /* avoid leaking bits */ reply.status = sane_open (name, &be_handle); - DBG (DBG_MSG, "process_request: sane_open returned: %s\n", + DBG (DBG_MSG, "process_request: sane_open returned: %s\n", sane_strstatus (reply.status)); } @@ -2062,7 +2070,7 @@ process_request (Wire * w) case SANE_NET_START: { SANE_Start_Reply reply; - int fd = -1, data_fd; + int fd = -1, data_fd = -1; h = decode_handle (w, "start"); if (h < 0) @@ -2087,9 +2095,36 @@ process_request (Wire * w) char text_addr[64]; int len; int error; + struct pollfd fds[1]; + int ret; - DBG (DBG_MSG, "process_request: waiting for data connection\n"); - data_fd = accept (fd, 0, 0); + fds->fd = fd; + fds->events = POLLIN; + + DBG (DBG_MSG, "process_request: waiting 4s for data connection\n"); + if(data_connect_timeout) + { + while (1) + { + ret = poll (fds, 1, data_connect_timeout); + if (ret < 0) + { + if (errno == EINTR) + continue; + else + { + DBG (DBG_ERR, "run_standalone: poll failed: %s\n", + strerror (errno)); + } + break; + } + break; + } + } + else + ret = 0; + if(ret >= 0) + data_fd = accept (fd, 0, 0); close (fd); /* Get address of remote host */ @@ -2112,7 +2147,7 @@ process_request (Wire * w) DBG (DBG_MSG, "process_request: access to data port from %s\n", text_addr); - + if (strcmp (text_addr, remote_ip) != 0) { DBG (DBG_ERR, "process_request: however, only %s is authorized\n", @@ -2129,14 +2164,41 @@ process_request (Wire * w) { struct sockaddr_in sin; int len; + int ret; + struct pollfd fds[1]; + + fds->fd = fd; + fds->events = POLLIN; DBG (DBG_MSG, "process_request: waiting for data connection\n"); - data_fd = accept (fd, 0, 0); + if(data_connect_timeout) + { + while (1) + { + ret = poll (fds, 1, data_connect_timeout); + if (ret < 0) + { + if (errno == EINTR) + continue; + else + { + DBG (DBG_ERR, "run_standalone: poll failed: %s\n", strerror (errno)); + } + break; + } + break; + } + } + else + ret = 0; + if(ret >= 0) + data_fd = accept (fd, 0, 0); + close (fd); /* Get address of remote host */ len = sizeof (sin); - if (getpeername (data_fd, (struct sockaddr *) &sin, + if (getpeername (data_fd, (struct sockaddr *) &sin, (socklen_t *) &len) < 0) { DBG (DBG_ERR, "process_request: getpeername failed: %s\n", @@ -2147,13 +2209,13 @@ process_request (Wire * w) if (memcmp (&remote_address, &sin.sin_addr, sizeof (remote_address)) != 0) { - DBG (DBG_ERR, + DBG (DBG_ERR, "process_request: access to data port from %s\n", inet_ntoa (sin.sin_addr)); - DBG (DBG_ERR, + DBG (DBG_ERR, "process_request: however, only %s is authorized\n", inet_ntoa (remote_address)); - DBG (DBG_ERR, + DBG (DBG_ERR, "process_request: configuration problem or attack?\n"); close (data_fd); data_fd = -1; @@ -2320,7 +2382,7 @@ handle_connection (int fd) reset_watchdog (); if (process_request (&wire) < 0) break; - } + } } static void @@ -2612,6 +2674,9 @@ saned_avahi_callback (AvahiClient *c, AvahiClientState state, void *userdata) case AVAHI_CLIENT_S_COLLISION: DBG (DBG_INFO, "saned_avahi_callback: AVAHI_CLIENT_S_COLLISION\n"); + if (avahi_group) + avahi_entry_group_reset (avahi_group); + break; case AVAHI_CLIENT_S_REGISTERING: DBG (DBG_INFO, "saned_avahi_callback: AVAHI_CLIENT_S_REGISTERING\n"); @@ -2735,6 +2800,26 @@ read_config (void) DBG (DBG_INFO, "read_config: data port range: %d - %d\n", data_port_lo, data_port_hi); } } + else if(strstr(config_line, "data_connect_timeout") != NULL) + { + optval = sanei_config_skip_whitespace (++optval); + if ((optval != NULL) && (*optval != '\0')) + { + val = strtol (optval, &endval, 10); + if (optval == endval) + { + DBG (DBG_ERR, "read_config: invalid value for data_connect_timeout\n"); + continue; + } + else if ((val < 0) || (val > 65535)) + { + DBG (DBG_ERR, "read_config: data_connect_timeout is invalid\n"); + continue; + } + data_connect_timeout = val; + DBG (DBG_INFO, "read_config: data connect timeout: %d\n", data_connect_timeout); + } + } } fclose (fp); DBG (DBG_INFO, "read_config: done reading config\n"); @@ -2964,93 +3049,129 @@ do_bindings (int *nfds, struct pollfd **fds) static void -run_standalone (char *user) +runas_user (char *user) { - struct pollfd *fds = NULL; - struct pollfd *fdp = NULL; - int nfds; - int fd = -1; - int i; - int ret; - uid_t runas_uid = 0; gid_t runas_gid = 0; struct passwd *pwent; gid_t *grplist = NULL; struct group *grp; int ngroups = 0; - FILE *pidfile; + int ret; - do_bindings (&nfds, &fds); + pwent = getpwnam(user); - if (run_mode != SANED_RUN_DEBUG) + if (pwent == NULL) { - if (user) - { - pwent = getpwnam(user); + DBG (DBG_ERR, "FATAL ERROR: user %s not found on system\n", user); + bail_out (1); + } - if (pwent == NULL) - { - DBG (DBG_ERR, "FATAL ERROR: user %s not found on system\n", user); - bail_out (1); - } + runas_uid = pwent->pw_uid; + runas_gid = pwent->pw_gid; - runas_uid = pwent->pw_uid; - runas_gid = pwent->pw_gid; + /* Get group list for runas_uid */ + grplist = (gid_t *)malloc(sizeof(gid_t)); - /* Get group list for runas_uid */ - grplist = (gid_t *)malloc(sizeof(gid_t)); + if (grplist == NULL) + { + DBG (DBG_ERR, "FATAL ERROR: cannot allocate memory for group list\n"); - if (grplist == NULL) - { - DBG (DBG_ERR, "FATAL ERROR: cannot allocate memory for group list\n"); + exit (1); + } - exit (1); - } + ngroups = 1; + grplist[0] = runas_gid; - ngroups = 1; - grplist[0] = runas_gid; + setgrent(); + while ((grp = getgrent()) != NULL) + { + int i = 0; - setgrent(); - while ((grp = getgrent()) != NULL) - { - int i = 0; + /* Already added current group */ + if (grp->gr_gid == runas_gid) + continue; - /* Already added current group */ - if (grp->gr_gid == runas_gid) - continue; + while (grp->gr_mem[i]) + { + if (strcmp(grp->gr_mem[i], user) == 0) + { + int need_to_add = 1, j; - while (grp->gr_mem[i]) + /* Make sure its not already in list */ + for (j = 0; j < ngroups; j++) { - if (strcmp(grp->gr_mem[i], user) == 0) - { - int need_to_add = 1, j; - - /* Make sure its not already in list */ - for (j = 0; j < ngroups; j++) - { - if (grp->gr_gid == grplist[i]) - need_to_add = 0; - } - if (need_to_add) - { - grplist = (gid_t *)realloc(grplist, - sizeof(gid_t)*ngroups+1); - if (grplist == NULL) - { - DBG (DBG_ERR, "FATAL ERROR: cannot reallocate memory for group list\n"); - - exit (1); - } - grplist[ngroups++] = grp->gr_gid; - } - } - i++; - } + if (grp->gr_gid == grplist[i]) + need_to_add = 0; + } + if (need_to_add) + { + grplist = (gid_t *)realloc(grplist, + sizeof(gid_t)*ngroups+1); + if (grplist == NULL) + { + DBG (DBG_ERR, "FATAL ERROR: cannot reallocate memory for group list\n"); + + exit (1); + } + grplist[ngroups++] = grp->gr_gid; + } } - endgrent(); + i++; } + } + endgrent(); + /* Drop privileges if requested */ + if (runas_uid > 0) + { + ret = setgroups(ngroups, grplist); + if (ret < 0) + { + DBG (DBG_ERR, "FATAL ERROR: could not set group list: %s\n", strerror(errno)); + + exit (1); + } + + free(grplist); + + ret = setegid (runas_gid); + if (ret < 0) + { + DBG (DBG_ERR, "FATAL ERROR: setegid to gid %d failed: %s\n", runas_gid, strerror (errno)); + + exit (1); + } + + ret = seteuid (runas_uid); + if (ret < 0) + { + DBG (DBG_ERR, "FATAL ERROR: seteuid to uid %d failed: %s\n", runas_uid, strerror (errno)); + + exit (1); + } + + DBG (DBG_WARN, "Dropped privileges to uid %d gid %d\n", runas_uid, runas_gid); + } +} + + +static void +run_standalone (char *user) +{ + struct pollfd *fds = NULL; + struct pollfd *fdp = NULL; + int nfds; + int fd = -1; + int i; + int ret; + + FILE *pidfile; + + do_bindings (&nfds, &fds); + + if (run_foreground == SANE_FALSE) + { DBG (DBG_MSG, "run_standalone: daemonizing now\n"); fd = open ("/dev/null", O_RDWR); @@ -3093,42 +3214,13 @@ run_standalone (char *user) setsid (); - /* Drop privileges if requested */ - if (runas_uid > 0) - { - ret = setgroups(ngroups, grplist); - if (ret < 0) - { - DBG (DBG_ERR, "FATAL ERROR: could not set group list: %s\n", strerror(errno)); - - exit (1); - } - - free(grplist); - - ret = setegid (runas_gid); - if (ret < 0) - { - DBG (DBG_ERR, "FATAL ERROR: setegid to gid %d failed: %s\n", runas_gid, strerror (errno)); - - exit (1); - } - - ret = seteuid (runas_uid); - if (ret < 0) - { - DBG (DBG_ERR, "FATAL ERROR: seteuid to uid %d failed: %s\n", runas_uid, strerror (errno)); - - exit (1); - } - - DBG (DBG_WARN, "Dropped privileges to uid %d gid %d\n", runas_uid, runas_gid); - } - signal(SIGINT, sig_int_term_handler); signal(SIGTERM, sig_int_term_handler); } + if (user) + runas_user(user); + #ifdef WITH_AVAHI DBG (DBG_INFO, "run_standalone: spawning Avahi process\n"); saned_avahi (fds, nfds); @@ -3187,13 +3279,13 @@ run_standalone (char *user) continue; } - if (run_mode == SANED_RUN_DEBUG) - break; /* We have the only connection we're going to handle */ - else - handle_client (fd); + handle_client (fd); + + if (run_once == SANE_TRUE) + break; /* We have handled the only connection we're going to handle */ } - if (run_mode == SANED_RUN_DEBUG) + if (run_once == SANE_TRUE) break; } @@ -3201,21 +3293,13 @@ run_standalone (char *user) close (fdp->fd); free (fds); - - if (run_mode == SANED_RUN_DEBUG) - { - if (fd > 0) - handle_connection (fd); - - bail_out(0); - } } static void run_inetd (char __sane_unused__ *sock) { - + int fd = -1; #ifdef HAVE_SYSTEMD @@ -3223,7 +3307,7 @@ run_inetd (char __sane_unused__ *sock) n = sd_listen_fds(0); - if (n > 1) + if (n > 1) { DBG (DBG_ERR, "run_inetd: Too many file descriptors (sockets) received from systemd!\n"); return; @@ -3236,7 +3320,7 @@ run_inetd (char __sane_unused__ *sock) } #endif - if (fd == -1) + if (fd == -1) { int dave_null; @@ -3299,12 +3383,14 @@ static void usage(char *me, int err) fprintf (stderr, "Usage: %s [OPTIONS]\n\n" " Options:\n\n" - " -a, --alone[=user] run standalone and fork in background as `user'\n" - " -d, --debug[=level] run foreground with output to stdout\n" - " and debug level `level' (default is 2)\n" - " -s, --syslog[=level] run foreground with output to syslog\n" - " and debug level `level' (default is 2)\n" - " -b, --bind=addr bind address `addr'\n" + " -a, --alone[=user] equal to `-l -D -u user'\n" + " -l, --listen run in standalone mode (listen for connection)\n" + " -u, --user=user run as `user'\n" + " -D, --daemonize run in background\n" + " -o, --once exit after first client disconnects\n" + " -d, --debug=level set debug level `level' (default is 2)\n" + " -e, --stderr output to stderr\n" + " -b, --bind=addr bind address `addr' (default all interfaces)\n" " -h, --help show this help message and exit\n", me); exit(err); @@ -3317,8 +3403,12 @@ static struct option long_options[] = /* These options set a flag. */ {"help", no_argument, 0, 'h'}, {"alone", optional_argument, 0, 'a'}, - {"debug", optional_argument, 0, 'd'}, - {"syslog", optional_argument, 0, 's'}, + {"listen", no_argument, 0, 'l'}, + {"user", required_argument, 0, 'u'}, + {"daemonize", no_argument, 0, 'D'}, + {"once", no_argument, 0, 'o'}, + {"debug", required_argument, 0, 'd'}, + {"stderr", no_argument, 0, 'e'}, {"bind", required_argument, 0, 'b'}, {0, 0, 0, 0 } }; @@ -3342,20 +3432,35 @@ main (int argc, char *argv[]) numchildren = 0; run_mode = SANED_RUN_INETD; + run_foreground = SANE_TRUE; + run_once = SANE_FALSE; - while((c = getopt_long(argc, argv,"ha::d::s::b:", long_options, &long_index )) != -1) + while((c = getopt_long(argc, argv,"ha::lu:Dod:eb:", long_options, &long_index )) != -1) { switch(c) { case 'a': run_mode = SANED_RUN_ALONE; + run_foreground = SANE_FALSE; + if (optarg) + user = optarg; + break; + case 'l': + run_mode = SANED_RUN_ALONE; + break; + case 'u': user = optarg; break; + case 'D': + run_foreground = SANE_FALSE; + break; + case 'o': + run_once = SANE_TRUE; + break; case 'd': + debug = atoi(optarg); + break; + case 'e': log_to_syslog = SANE_FALSE; - case 's': - run_mode = SANED_RUN_DEBUG; - if(optarg) - debug = atoi(optarg); break; case 'b': bind_addr = optarg; @@ -3405,7 +3510,7 @@ main (int argc, char *argv[]) DBG (DBG_WARN, "saned from %s ready\n", PACKAGE_STRING); } - if ((run_mode == SANED_RUN_ALONE) || (run_mode == SANED_RUN_DEBUG)) + if (run_mode == SANED_RUN_ALONE) { run_standalone(user); } |