saned: reorganize flags, remove run_mode SANED_RUN_DEBUG

Flags like -a, -d and -s have many overlap effects. This patch restricts
the effect of flags to a simple action.

New -u (user) flag replaces -a optional argument for running saned as a different user.
The code that retrieve the user info and drop privileges migrated to runas_user().
As a side effect, PID file can be created even if getting user info fails.

New -l (listen) flag sets run_mode to standalone.
New -D (daemonize) flag daemonizes saned after bind.
New -o (once) make saned exit after the first client disconnects.
Flag -s (syslog) is gone. Previous behavior can be reproduced with '-a -d level -o -f'.
New -e (stderr) flag for redirecting output to stderr, instead of syslog.

Flag -d (debug) now only sets the debug level and argument is required. Previous behavior
can be reproduced with '-a -d level -o -f -e'.

The run_mode SANED_RUN_DEBUG and SANED_RUN_ALONE shared most of its code
path. With the new flags dealing with their difference, SANED_RUN_DEBUG is gone.

Flag '-a' still works as before but it can be replaced by '-l -D -u user'.

Current uses of -d (debug) or -s (syslog) will break.

Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
merge-requests/1/head
Luiz Angelo Daros de Luca 2017-09-18 04:25:37 -03:00 zatwierdzone przez Olaf Meeuwissen
rodzic c9356cb184
commit 5288dd5f61
3 zmienionych plików z 207 dodań i 165 usunięć

8
NEWS
Wyświetl plik

@ -1,4 +1,12 @@
-*-Mode: outline-*-
New with the development version, not yet released:
* Saned options where reorganized (See "man 8 saned" for details):
o New: -l (listen), -D (daemonize), -o (once), -e (stderr), -u (user).
o Removed: -s (syslog). Use '-a -d level -o -f' for the old behavior.
o Changed: -d (debug). Use '-a -d level -o -f -e' for the old behavior.
New with 1.0.27 (see Note 1), released 2017-05-22:
* Significant enhancements to canon_dr, epjitsu, epsonds, fujitsu,

Wyświetl plik

@ -6,15 +6,21 @@ saned \- SANE network daemon
.B saned
.B [ \-a
.I [ username ]
.B ]
.B [ \-u
.I username
.B ]
.B [ \-b
.I address
.B ]
.B | \-d
.I [ n ]
.B | \-s
.I [ n ]
.B | \-h
.B [ \-l ]
.B [ \-D ]
.B [ \-o ]
.B [ \-d
.I n
.B ]
.B [ \-e ]
.B [ \-h ]
.SH DESCRIPTION
.B saned
is the SANE (Scanner Access Now Easy) daemon that allows remote clients
@ -22,51 +28,59 @@ to access image acquisition devices available on the local host.
.SH OPTIONS
.PP
The
.B \-a
.B \-l
flag requests that
.B saned
run in standalone daemon mode. In this mode,
.B saned
will detach from the console and run in the background, listening for incoming
client connections;
will listening for incoming client connections;
.B inetd
is not required for
.B saned
operations in this mode. If the optional
.B username
is given after
.B \-a
,
operations in this mode. The
.B \-b
flag can control which address
.B saned
will drop root privileges and run as this user (and group).
will bind. The
.B \-u
.I username
flag requests that
.B saned
drop root privileges and run as this user (and group) after bind.
The
.B \-D
flag will request
.B saned
to detach from the console and run in the background.
The flag
.B \-a
is equals to
.B \-l \-B \-u
.I username
.
.PP
The
.B \-e
flag will request that
.B saned
output to stderr instead of syslog.
.PP
The
.B \-d
and
.B \-s
flags request that
flag sets the debug level of
.B saned
run in debug mode (as opposed to
.BR inetd (8)
daemon mode). In this mode,
.B saned
explicitly waits for a connection request. When compiled with
debugging enabled, these flags may be followed by a number to request
. When compiled with debugging enabled, these flags may be followed by a number to request
debug info. The larger the number, the more verbose the debug output.
E.g.,
.B \-d128
will request printing of all debug info. Debug level 0 means no debug output
at all. The default value is 2. If flag
.B \-d
is used, the debug messages will be printed to stderr while
.B \-s
requests using syslog.
at all. The default value is 2.
.PP
The
.B \-b
.B \-o
flag requests that
.B saned
bind to a specific address.
exits after the first client disconnects. Useful for debugging.
.PP
If
.B saned

Wyświetl plik

@ -251,6 +251,8 @@ 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;
@ -299,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
@ -3045,6 +3045,114 @@ do_bindings (int *nfds, struct pollfd **fds)
#endif /* SANED_USES_AF_INDEP */
static void
runas_user (char *user)
{
uid_t runas_uid = 0;
gid_t runas_gid = 0;
struct passwd *pwent;
gid_t *grplist = NULL;
struct group *grp;
int ngroups = 0;
int ret;
pwent = getpwnam(user);
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;
/* 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");
exit (1);
}
ngroups = 1;
grplist[0] = runas_gid;
setgrent();
while ((grp = getgrent()) != NULL)
{
int i = 0;
/* 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;
/* 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++;
}
}
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)
{
@ -3055,84 +3163,12 @@ run_standalone (char *user)
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;
do_bindings (&nfds, &fds);
if (run_mode != SANED_RUN_DEBUG)
if (run_foreground == SANE_FALSE)
{
if (user)
{
pwent = getpwnam(user);
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;
/* 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");
exit (1);
}
ngroups = 1;
grplist[0] = runas_gid;
setgrent();
while ((grp = getgrent()) != NULL)
{
int i = 0;
/* 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;
/* 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++;
}
}
endgrent();
}
DBG (DBG_MSG, "run_standalone: daemonizing now\n");
fd = open ("/dev/null", O_RDWR);
@ -3175,42 +3211,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);
@ -3269,13 +3276,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;
}
@ -3283,14 +3290,6 @@ run_standalone (char *user)
close (fdp->fd);
free (fds);
if (run_mode == SANED_RUN_DEBUG)
{
if (fd > 0)
handle_connection (fd);
bail_out(0);
}
}
@ -3381,12 +3380,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 stderr\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] equals 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);
@ -3399,8 +3400,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 }
};
@ -3424,20 +3429,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;
@ -3487,7 +3507,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);
}