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-*- -*-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: New with 1.0.27 (see Note 1), released 2017-05-22:
* Significant enhancements to canon_dr, epjitsu, epsonds, fujitsu, * Significant enhancements to canon_dr, epjitsu, epsonds, fujitsu,

Wyświetl plik

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

Wyświetl plik

@ -251,6 +251,8 @@ static Wire wire;
static int num_handles; static int num_handles;
static int debug; static int debug;
static int run_mode; static int run_mode;
static int run_foreground;
static int run_once;
static int data_connect_timeout = 4000; static int data_connect_timeout = 4000;
static Handle *handle; static Handle *handle;
static char *bind_addr; static char *bind_addr;
@ -299,9 +301,7 @@ static SANE_Bool log_to_syslog = SANE_TRUE;
static int process_request (Wire * w); static int process_request (Wire * w);
#define SANED_RUN_INETD 0 #define SANED_RUN_INETD 0
#define SANED_RUN_DEBUG 1 #define SANED_RUN_ALONE 1
#define SANED_RUN_ALONE 2
#define DBG_ERR 1 #define DBG_ERR 1
#define DBG_WARN 2 #define DBG_WARN 2
@ -3045,6 +3045,114 @@ do_bindings (int *nfds, struct pollfd **fds)
#endif /* SANED_USES_AF_INDEP */ #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 static void
run_standalone (char *user) run_standalone (char *user)
{ {
@ -3055,84 +3163,12 @@ run_standalone (char *user)
int i; int i;
int ret; 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; FILE *pidfile;
do_bindings (&nfds, &fds); 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"); DBG (DBG_MSG, "run_standalone: daemonizing now\n");
fd = open ("/dev/null", O_RDWR); fd = open ("/dev/null", O_RDWR);
@ -3175,42 +3211,13 @@ run_standalone (char *user)
setsid (); 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(SIGINT, sig_int_term_handler);
signal(SIGTERM, sig_int_term_handler); signal(SIGTERM, sig_int_term_handler);
} }
if (user)
runas_user(user);
#ifdef WITH_AVAHI #ifdef WITH_AVAHI
DBG (DBG_INFO, "run_standalone: spawning Avahi process\n"); DBG (DBG_INFO, "run_standalone: spawning Avahi process\n");
saned_avahi (fds, nfds); saned_avahi (fds, nfds);
@ -3269,13 +3276,13 @@ run_standalone (char *user)
continue; continue;
} }
if (run_mode == SANED_RUN_DEBUG) handle_client (fd);
break; /* We have the only connection we're going to handle */
else if (run_once == SANE_TRUE)
handle_client (fd); break; /* We have handled the only connection we're going to handle */
} }
if (run_mode == SANED_RUN_DEBUG) if (run_once == SANE_TRUE)
break; break;
} }
@ -3283,14 +3290,6 @@ run_standalone (char *user)
close (fdp->fd); close (fdp->fd);
free (fds); 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, fprintf (stderr,
"Usage: %s [OPTIONS]\n\n" "Usage: %s [OPTIONS]\n\n"
" Options:\n\n" " Options:\n\n"
" -a, --alone[=user] run standalone and fork in background as `user'\n" " -a, --alone[=user] equals to `-l -D -u user'\n"
" -d, --debug[=level] run foreground with output to stderr\n" " -l, --listen run in standalone mode (listen for connection)\n"
" and debug level `level' (default is 2)\n" " -u, --user=user run as `user'\n"
" -s, --syslog[=level] run foreground with output to syslog\n" " -D, --daemonize run in background\n"
" and debug level `level' (default is 2)\n" " -o, --once exit after first client disconnects\n"
" -b, --bind=addr bind address `addr'\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); " -h, --help show this help message and exit\n", me);
exit(err); exit(err);
@ -3399,8 +3400,12 @@ static struct option long_options[] =
/* These options set a flag. */ /* These options set a flag. */
{"help", no_argument, 0, 'h'}, {"help", no_argument, 0, 'h'},
{"alone", optional_argument, 0, 'a'}, {"alone", optional_argument, 0, 'a'},
{"debug", optional_argument, 0, 'd'}, {"listen", no_argument, 0, 'l'},
{"syslog", optional_argument, 0, 's'}, {"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'}, {"bind", required_argument, 0, 'b'},
{0, 0, 0, 0 } {0, 0, 0, 0 }
}; };
@ -3424,20 +3429,35 @@ main (int argc, char *argv[])
numchildren = 0; numchildren = 0;
run_mode = SANED_RUN_INETD; 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) { switch(c) {
case 'a': case 'a':
run_mode = SANED_RUN_ALONE; 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; user = optarg;
break; break;
case 'D':
run_foreground = SANE_FALSE;
break;
case 'o':
run_once = SANE_TRUE;
break;
case 'd': case 'd':
debug = atoi(optarg);
break;
case 'e':
log_to_syslog = SANE_FALSE; log_to_syslog = SANE_FALSE;
case 's':
run_mode = SANED_RUN_DEBUG;
if(optarg)
debug = atoi(optarg);
break; break;
case 'b': case 'b':
bind_addr = optarg; bind_addr = optarg;
@ -3487,7 +3507,7 @@ main (int argc, char *argv[])
DBG (DBG_WARN, "saned from %s ready\n", PACKAGE_STRING); 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); run_standalone(user);
} }