diff options
author | runge <runge> | 2005-02-09 04:33:20 +0000 |
---|---|---|
committer | runge <runge> | 2005-02-09 04:33:20 +0000 |
commit | 5b18d40136b20695b4048a69fcfbd0631ad7bb81 (patch) | |
tree | 490864d58579cd67f27ed8ee4fa3684864926725 /x11vnc/x11vnc.c | |
parent | b04336f947921e444daa5d922d9f07f2553fac98 (diff) | |
download | libtdevnc-5b18d40136b20695b4048a69fcfbd0631ad7bb81.tar.gz libtdevnc-5b18d40136b20695b4048a69fcfbd0631ad7bb81.zip |
x11vnc -users, fix -solid on gnome/kde, configure.ac pwd.h wait.h and utmpx.h
Diffstat (limited to 'x11vnc/x11vnc.c')
-rw-r--r-- | x11vnc/x11vnc.c | 918 |
1 files changed, 793 insertions, 125 deletions
diff --git a/x11vnc/x11vnc.c b/x11vnc/x11vnc.c index fbf896a..af15260 100644 --- a/x11vnc/x11vnc.c +++ b/x11vnc/x11vnc.c @@ -137,11 +137,17 @@ #define LIBVNCSERVER_HAVE_XSHM 1 #define LIBVNCSERVER_HAVE_XTEST 1 + +#define LIBVNCSERVER_HAVE_PWD_H 1 +#define LIBVNCSERVER_HAVE_SYS_WAIT_H 1 +#define LIBVNCSERVER_HAVE_UTMPX_H 1 + /* #define LIBVNCSERVER_HAVE_LIBXINERAMA 1 #define LIBVNCSERVER_HAVE_XFIXES 1 #define LIBVNCSERVER_HAVE_XDAMAGE 1 */ + #endif /* OLD_TREE */ #include <unistd.h> @@ -215,6 +221,17 @@ extern int h_errno; #include <arpa/inet.h> #endif +/* XXX autoconf */ +#if LIBVNCSERVER_HAVE_PWD_H +#include <pwd.h> +#endif +#if LIBVNCSERVER_HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif +#if LIBVNCSERVER_HAVE_UTMPX_H +#include <utmpx.h> +#endif + /* * overlay/multi-depth screen reading support * undef SOLARIS_OVERLAY or IRIX_OVERLAY if there are problems building. @@ -273,7 +290,7 @@ static int xdamage_base_event_type; #endif /* date +'lastmod: %Y-%m-%d' */ -char lastmod[] = "0.7.1pre lastmod: 2005-02-05"; +char lastmod[] = "0.7.1pre lastmod: 2005-02-08"; /* X display info */ @@ -371,6 +388,10 @@ time_t last_event, last_input, last_client = 0; /* last client to move pointer */ rfbClientPtr last_pointer_client = NULL; +int accepted_client = 0; +int client_count = 0; +int clients_served = 0; + /* more transient kludge variables: */ int cursor_x, cursor_y; /* x and y from the viewer(s) */ int got_user_input = 0; @@ -433,6 +454,7 @@ void initialize_polling_images(void); void initialize_signals(void); void initialize_tiles(void); void initialize_speeds(void); +void clean_shm(int); void free_tiles(void); void initialize_watch_bell(void); void initialize_xinerama(void); @@ -545,6 +567,8 @@ int shared = 0; /* share vnc display. */ int deny_all = 0; /* global locking of new clients */ int accept_remote_cmds = 1; /* -noremote */ int safe_remote_only = 0; /* -safer, -unsafe */ +int started_as_root = 0; +char *users_list = NULL; /* -users */ char *allow_list = NULL; /* for -allow and -localhost */ char *allow_once = NULL; /* one time -allow */ char *accept_cmd = NULL; /* for -accept */ @@ -752,6 +776,17 @@ void lowercase(char *str) { } } +char *lblanks(char *str) { + char *p = str; + while (*p) { + if (! isspace(*p)) { + break; + } + p++; + } + return p; +} + int scan_hexdec(char *str, unsigned long *num) { if (sscanf(str, "0x%lx", num) != 1) { if (sscanf(str, "%ld", num) != 1) { @@ -852,6 +887,424 @@ char *bitprint(unsigned int st, int nbits) { return str; /* take care to use or copy immediately */ } +char *get_user_name(void) { + char *user = NULL; + + user = getenv("USER"); + if (user == NULL) { + user = getenv("LOGNAME"); + } + +#if LIBVNCSERVER_HAVE_PWD_H + if (user == NULL) { + struct passwd *pw = getpwuid(getuid()); + if (pw) { + user = pw->pw_name; + } + } +#endif + + if (user) { + return(strdup(user)); + } else { + return(strdup("unknown-user")); + } +} + +char *get_home_dir(void) { + char *home = NULL; + + home = getenv("HOME"); + +#if LIBVNCSERVER_HAVE_PWD_H + if (home == NULL) { + struct passwd *pw = getpwuid(getuid()); + if (pw) { + home = pw->pw_dir; + } + } +#endif + + if (home) { + return(strdup(home)); + } else { + return(strdup("/")); + } +} + +char *get_shell(void) { + char *shell = NULL; + + shell = getenv("SHELL"); + +#if LIBVNCSERVER_HAVE_PWD_H + if (shell == NULL) { + struct passwd *pw = getpwuid(getuid()); + if (pw) { + shell = pw->pw_shell; + } + } +#endif + + if (shell) { + return(strdup(shell)); + } else { + return(strdup("/bin/sh")); + } +} + +int switch_user(char *); +void try_to_switch_users(void); +char *guess_desktop(void); + +void switch_user_dummy(void) { + ; /* dummy does nothing */ +} +void switch_user_solid_bg(void) { + /* we have switched users, some things to do. */ + if (use_solid_bg && client_count) { + solid_bg(0); + } +} + +void check_switched_user (void) { + static time_t sched_switched_user = 0; + static int did_solid = 0; + static int did_dummy = 0; + int delay = 15; + time_t now = time(0); + + if (started_as_root == 1 && users_list) { + try_to_switch_users(); + if (started_as_root == 2) { + /* + * schedule the switch_user_tasks() call + * 15 secs is for piggy desktops to start up. + * might not be enough for slow machines... + */ + sched_switched_user = now; + did_dummy = 0; + did_solid = 0; + /* add other activities */ + } + } + if (! sched_switched_user) { + return; + } + + if (! did_dummy) { + switch_user_dummy(); + did_dummy = 1; + } + if (! did_solid) { + int doit = 0; + char *ss = solid_str; + if (now >= sched_switched_user + delay) { + doit = 1; + } else if (ss && strstr(ss, "root:") == ss) { + if (now >= sched_switched_user + 3) { + doit = 1; + } + } else if (strcmp("root", guess_desktop())) { + usleep(1000 * 1000); + doit = 1; + } + if (doit) { + switch_user_solid_bg(); + did_solid = 1; + } + } + + if (did_dummy && did_solid) { + sched_switched_user = 0; + } +} + +int guess_user_and_switch(char *str) { +#if LIBVNCSERVER_HAVE_UTMPX_H + char *q, *dstr, *d = DisplayString(dpy); + char *allowed = NULL; + int i, ret = 0, max = 300; + + if (strstr(str, "guess=") == str) { + char *allowed = strchr(str, '='); + allowed++; + } + + /* pick out ":N" */ + dstr = strchr(d, ':'); + if (! dstr) { + return 0; + } + q = strchr(dstr, '.'); + if (q) { + *q = '\0'; + } + + /* look over the utmpx entries looking for this display */ + setutxent(); + for (i=0; i<max; i++) { + char *str; + struct utmpx *utx = getutxent(); + + if (! utx) { + break; + } + + str = lblanks(utx->ut_user); + if (*str == '\0') { + continue; /* blank user */ + } + if (allowed) { + char *p, *t = strdup(allowed); + int ok = 0; + p = strtok(t, ","); + while (p) { + if (!strcmp(p, utx->ut_user)) { + ok = 1; + } + p = strtok(NULL, ","); + } + free(t); + if (! ok) { + continue; + } + } + if (!strcmp(utx->ut_user, "guess")) { + continue; /* never... */ + } + + /* try the line for leading :N */ + str = lblanks(utx->ut_line); + if (strstr(str, dstr) == str) { + int n = strlen(dstr); + if (isdigit(*(str+n))) { + continue; /* :1 vs. :10 */ + } else if (switch_user(utx->ut_user)) { + rfbLog("switched to guessed user: %s\n", + utx->ut_user); + ret = 1; + break; + } else { + continue; + } + } + + /* try the host for leading :N */ + str = lblanks(utx->ut_host); + if (strstr(str, dstr) == str) { + int n = strlen(dstr); + if (isdigit(*(str+n))) { + continue; /* :1 vs. :10 */ + } else if (switch_user(utx->ut_user)) { + rfbLog("switched to guessed user: %s\n", + utx->ut_user); + ret = 1; + break; + } else { + continue; + } + } + } + endutxent(); + + return ret; +#else + return 0; +#endif +} + +int switch_user(char *user) { + int force = 0, numerical = 1; + uid_t uid = 0; + char *q, *name, *home; + + if (*user == '+') { + force = 1; + user++; + } + + if (!strcmp(user, "guess") || strstr(user, "guess=") == user) { + return guess_user_and_switch(user); + } + + q = user; + while (*q) { + if (! isdigit(*q)) { + numerical = 0; + break; + } + q++; + } + +#if LIBVNCSERVER_HAVE_PWD_H + if (numerical) { + int u = atoi(user); + struct passwd *pw; + + if (u > 0) { + uid = (uid_t) u; + } else { + return 0; + } + pw = getpwuid(uid); + if (pw) { + name = pw->pw_name; + home = pw->pw_dir; + } else { + return 0; + } + } else { + struct passwd *pw = getpwnam(user); + if (pw) { + uid = pw->pw_uid; + name = pw->pw_name; + home = pw->pw_dir; + } else { + return 0; + } + } +#else + return 0; +#endif + if (! uid) { + return 0; + } + + if (! force) { +#if LIBVNCSERVER_HAVE_FORK && LIBVNCSERVER_HAVE_SYS_WAIT_H + pid_t pid, pidw; + int st; + + /* + * We fork here and try to open the display again as the + * new user. Unreadable XAUTHORITY could be a problem... + * This is not really needed since we have DISPLAY open + * but: 1) is a good indicator this user owns the session + * and 2) some activities do spawn new X apps, e.g. + * xmessage(1), etc. + */ + if ((pid = fork()) > 0) { + ; + } else if (pid == -1) { + fprintf(stderr, "could not fork\n"); + rfbLogPerror("fork"); + return 0; + } else { + Display *dpy2 = 0; + char *xauth = getenv("XAUTHORITY"); +#if LIBVNCSERVER_HAVE_SETUID + if (setuid(uid) != 0) { + exit(1); /* fail */ + } +#else + exit(1); +#endif + if (xauth && access(xauth, R_OK) != 0) { + *(xauth-2) = '_'; /* yow */ + } + set_env("USER", name); + set_env("LOGNAME", name); + set_env("HOME", home); + fclose(stderr); + dpy2 = XOpenDisplay(DisplayString(dpy)); + if (dpy2) { + XCloseDisplay(dpy2); + exit(0); /* success */ + } else { + exit(2); /* fail */ + } + } + + /* see what the child says: */ + pidw = waitpid(pid, &st, 0); + if (pidw == pid && WIFEXITED(st) && WEXITSTATUS(st) == 0) { + force = 1; + } +#else + force = 1; +#endif + } + + if (force) { + char *xauth = getenv("XAUTHORITY"); + + /* + * OK tricky here, we need to free the shm... otherwise + * we won't be able to delete it as the other user... + */ +#if !LIBVNCSERVER_HAVE_SETUID + return 0; +#else + if (using_shm) { + clean_shm(0); + free_tiles(); + } + if (setuid(uid) != 0) { + if (using_shm) { + /* 2 means we did clean_shm and free_tiles */ + do_new_fb(2); + } + return 0; + } +#endif + if (using_shm) { + do_new_fb(2); + } + + if (xauth && access(xauth, R_OK) != 0) { + *(xauth-2) = '_'; /* yow */ + } + set_env("USER", name); + set_env("LOGNAME", name); + set_env("HOME", home); + return 1; + } else { + return 0; + } +} + +void try_to_switch_users(void) { + static time_t last_try = 0; + time_t now = time(0); + char *users, *p; + + if (getuid() && geteuid()) { + rfbLog("try_to_switch_users: not root\n"); + started_as_root = 2; + return; + } + if (!last_try) { + last_try = now; + } else if (now <= last_try + 2) { + /* try every 3 secs or so */ + return; + } + last_try = now; + + users = strdup(users_list); + + if (strstr(users, "guess=") == users) { + if (switch_user(users)) { + started_as_root = 2; + rfbLog("try_to_switch_users: now %s\n", p); + } + free(users); + return; + } + + p = strtok(users, ","); + while (p) { + if (switch_user(p)) { + started_as_root = 2; + rfbLog("try_to_switch_users: now %s\n", p); + break; + } + p = strtok(NULL, ","); + } + free(users); +} + /* * Simple utility to map host name to dotted IP address. Ignores aliases. * Up to caller to free returned string. @@ -1318,7 +1771,7 @@ void clean_shm(int quick) { break; } } - if (!quiet && !quick) { + if (!quiet) { rfbLog("deleted %d tile_row polling images.\n", cnt); } } @@ -1356,62 +1809,13 @@ void clean_up_exit (int ret) { exit(ret); } -/* - * General problem handler - */ -static void interrupted (int sig) { - exit_sig = sig; - if (exit_flag) { - exit_flag++; - if (use_threads) { - usleep2(250 * 1000); - } else if (exit_flag <= 2) { - return; - } - exit(4); - } - exit_flag++; - if (sig == 0) { - fprintf(stderr, "caught X11 error:\n"); - } else { - fprintf(stderr, "caught signal: %d\n", sig); - } - if (sig == SIGINT) { - shut_down = 1; - return; - } - - X_UNLOCK; - - /* remove the shm areas with quick=1: */ - clean_shm(1); - - /* X keyboard cleanups */ - delete_added_keycodes(); - - if (clear_mods == 1) { - clear_modifiers(0); - } else if (clear_mods == 2) { - clear_keys(); - } - if (no_autorepeat) { - autorepeat(1); - } - if (use_solid_bg) { - solid_bg(1); - } - - if (sig) { - exit(2); - } -} - /* X11 error handlers */ static XErrorHandler Xerror_def; static XIOErrorHandler XIOerr_def; XErrorEvent *trapped_xerror_event; int trapped_xerror = 0; +int trapped_xioerror = 0; int trapped_getimage_xerror = 0; int trap_xerror(Display *d, XErrorEvent *error) { @@ -1420,12 +1824,19 @@ int trap_xerror(Display *d, XErrorEvent *error) { return 0; } +int trap_xioerror(Display *d) { + trapped_xioerror = 1; + return 0; +} + int trap_getimage_xerror(Display *d, XErrorEvent *error) { trapped_getimage_xerror = 1; trapped_xerror_event = error; return 0; } +void interrupted(int); + static int Xerror(Display *d, XErrorEvent *error) { X_UNLOCK; interrupted(0); @@ -1434,7 +1845,7 @@ static int Xerror(Display *d, XErrorEvent *error) { static int XIOerr(Display *d) { X_UNLOCK; - interrupted(0); + interrupted(-1); return (*XIOerr_def)(d); } @@ -1473,6 +1884,63 @@ char *xerror_string(XErrorEvent *error) { } } +/* + * General problem handler + */ +void interrupted (int sig) { + exit_sig = sig; + if (exit_flag) { + exit_flag++; + if (use_threads) { + usleep2(250 * 1000); + } else if (exit_flag <= 2) { + return; + } + exit(4); + } + exit_flag++; + if (sig == 0) { + fprintf(stderr, "caught X11 error:\n"); + } else if (sig == -1) { + fprintf(stderr, "caught XIO error:\n"); + } else { + fprintf(stderr, "caught signal: %d\n", sig); + } + if (sig == SIGINT) { + shut_down = 1; + return; + } + + X_UNLOCK; + + /* remove the shm areas with quick=1: */ + clean_shm(1); + + if (sig == -1) { + /* not worth trying any more cleanup, X server probably gone */ + exit(3); + } + + /* X keyboard cleanups */ + delete_added_keycodes(); + + if (clear_mods == 1) { + clear_modifiers(0); + } else if (clear_mods == 2) { + clear_keys(); + } + if (no_autorepeat) { + autorepeat(1); + } + if (use_solid_bg) { + solid_bg(1); + } + + if (sig) { + exit(2); + } +} + /* trapping utility to check for a valid window: */ int valid_window(Window win) { XErrorHandler old_handler; @@ -1563,10 +2031,6 @@ void initialize_signals(void) { * routines for handling incoming, outgoing, etc connections */ -static int accepted_client = 0; -static int client_count = 0; -static int clients_served = 0; - /* * check that all clients are in RFB_NORMAL state */ @@ -3189,16 +3653,9 @@ void initialize_remap(char *infile) { rewind(in); } while (fgets(line, 256, in) != NULL) { - int blank = 1, isbtn = 0; - p = line; - while (*p) { - if (! isspace(*p)) { - blank = 0; - break; - } - p++; - } - if (blank) { + int isbtn = 0; + p = lblanks(line); + if (*p == '\0') { continue; } if (strchr(line, '#')) { @@ -7987,6 +8444,8 @@ char *process_remote_cmd(char *cmd, int stringonly) { } } else if (!strcmp(p, "auth")) { snprintf(buf, bufn, "aro=%s:%s", p, NONUL(auth_file)); + } else if (!strcmp(p, "users")) { + snprintf(buf, bufn, "aro=%s:%s", p, NONUL(users_list)); } else if (!strcmp(p, "rootshift")) { snprintf(buf, bufn, "aro=%s:%d", p, rootshift); } else if (!strcmp(p, "scale_str")) { @@ -9156,6 +9615,10 @@ int get_which_cursor(void) { which = CURS_TERM; } else if (strstr(winfo.res_class, "text")) { which = CURS_TERM; + } else if (strstr(winfo.res_name, "onsole")) { + which = CURS_TERM; + } else if (strstr(winfo.res_class, "onsole")) { + which = CURS_TERM; } } } @@ -9796,7 +10259,8 @@ void do_new_fb(int reset_mem) { rfbLog(" not work, do not use -threads if problems arise.\n"); } - if (reset_mem) { + if (reset_mem == 1) { + /* reset_mem == 2 is a hack for changing users... */ clean_shm(0); free_tiles(); } @@ -10655,13 +11119,45 @@ void initialize_screen(int *argc, char **argv, XImage *fb) { /* -- solid.c -- */ -int do_cmd(char *cmd) { +void usr_bin_path(int restore) { + static char *oldpath = NULL; + char *newpath; + char addpath[] = "/usr/bin:/bin:"; + + if (restore) { + if (oldpath) { + set_env("PATH", oldpath); + free(oldpath); + oldpath = NULL; + } + return; + } + + if (getenv("PATH")) { + oldpath = strdup(getenv("PATH")); + } else { + oldpath = strdup("/usr/bin"); + } + newpath = (char *) malloc(strlen(oldpath) + strlen(addpath) + 1); + newpath[0] = '\0'; + strcat(newpath, addpath); + strcat(newpath, oldpath); + set_env("PATH", newpath); + free(newpath); +} + +int dt_cmd(char *cmd) { int rc; + if (!cmd || *cmd == '\0') { return 0; } + rfbLog("running command:\n %s\n", cmd); + usr_bin_path(0); rc = system(cmd); + usr_bin_path(1); + if (rc >= 256) { rc = rc/256; } @@ -10674,8 +11170,14 @@ char *cmd_output(char *cmd) { char line[1024]; int rc; + if (!cmd || *cmd == '\0') { + return ""; + } + rfbLog("running pipe:\n %s\n", cmd); + usr_bin_path(0); p = popen(cmd, "r"); + usr_bin_path(1); output[0] = '\0'; @@ -10826,12 +11328,12 @@ void solid_gnome(char *color) { cmd = (char *)malloc(strlen(set_option) - 2 + strlen(orig_option) + 1); sprintf(cmd, set_option, orig_option); - do_cmd(cmd); + dt_cmd(cmd); free(cmd); cmd = (char *)malloc(strlen(set_color) - 2 + strlen(orig_color) + 1); sprintf(cmd, set_color, orig_color); - do_cmd(cmd); + dt_cmd(cmd); free(cmd); return; } @@ -10860,24 +11362,42 @@ void solid_gnome(char *color) { rfbLog("bad color: %s\n", color); return; } - cmd = (char *)malloc(strlen(set_color) - 2 + strlen(color) + 1); + cmd = (char *)malloc(strlen(set_color) + strlen(color) + 1); sprintf(cmd, set_color, color); - do_cmd(cmd); + dt_cmd(cmd); free(cmd); - cmd = (char *)malloc(strlen(set_option) - 2 + strlen("none") + 1); + + cmd = (char *)malloc(strlen(set_option) + strlen("none") + 1); sprintf(cmd, set_option, "none"); - do_cmd(cmd); + dt_cmd(cmd); free(cmd); } void solid_kde(char *color) { - char set_color[] = "dcop kdesktop KBackgroundIface setColor '%s' 1"; - char bg_off[] = "dcop kdesktop KBackgroundIface setBackgroundEnabled 0"; - char bg_on[] = "dcop kdesktop KBackgroundIface setBackgroundEnabled 1"; - char *cmd; + char set_color[] = + "dcop --user '%s' kdesktop KBackgroundIface setColor '%s' 1"; + char bg_off[] = + "dcop --user '%s' kdesktop KBackgroundIface setBackgroundEnabled 0"; + char bg_on[] = + "dcop --user '%s' kdesktop KBackgroundIface setBackgroundEnabled 1"; + char *cmd, *user = NULL; + int len; + + user = get_user_name(); + if (strstr(user, "'") != NULL) { + rfbLog("bad user: %s\n", user); + free(user); + return; + } if (! color) { - do_cmd(bg_on); + len = strlen(bg_on) + strlen(user) + 1; + cmd = (char *)malloc(len); + sprintf(cmd, bg_on, user); + dt_cmd(cmd); + free(cmd); + free(user); + return; } @@ -10886,11 +11406,18 @@ void solid_kde(char *color) { return; } - cmd = (char *)malloc(strlen(set_color) - 2 + strlen(color) + 1); - sprintf(cmd, set_color, color); - do_cmd(cmd); - do_cmd(bg_off); + len = strlen(set_color) + strlen(user) + strlen(color) + 1; + cmd = (char *)malloc(len); + sprintf(cmd, set_color, user, color); + dt_cmd(cmd); free(cmd); + + len = strlen(bg_off) + strlen(user) + 1; + cmd = (char *)malloc(len); + sprintf(cmd, bg_off, user); + dt_cmd(cmd); + free(cmd); + free(user); } char *guess_desktop() { @@ -10912,6 +11439,11 @@ void solid_bg(int restore) { static char *prev_str; char *dtname, *color; + if (started_as_root == 1 && users_list) { + /* we are still root, don't try. */ + return; + } + if (restore) { if (! solid_on) { return; @@ -11644,6 +12176,7 @@ void shm_clean(XShmSegmentInfo *shm, XImage *xim) { #endif if (xim != NULL) { XDestroyImage(xim); + xim = NULL; } X_UNLOCK; @@ -13669,6 +14202,7 @@ void run_gui(char *gui_xdisplay, int connect_to_x11vnc, pid_t parent) { ":/usr/X11R6/bin:/usr/openwin/bin:/usr/dt/bin"; char cmd[100]; char *wish = NULL, *orig_path, *full_path, *tpath, *p; + char *old_xauth = NULL; int try_max = 4, sleep = 300; pid_t mypid = getpid(); FILE *pipe, *tmpf; @@ -13687,7 +14221,14 @@ void run_gui(char *gui_xdisplay, int connect_to_x11vnc, pid_t parent) { int rc, i; rfbLogEnable(0); if (! client_connect_file) { + if (getenv("XAUTHORITY") != NULL) { + old_xauth = strdup(getenv("XAUTHORITY")); + } dpy = XOpenDisplay(x11vnc_xdisplay); + if (! dpy && auth_file) { + set_env("XAUTHORITY", auth_file); + dpy = XOpenDisplay(x11vnc_xdisplay); + } if (! dpy && ! x11vnc_xdisplay) { x11vnc_xdisplay = strdup(":0"); dpy = XOpenDisplay(x11vnc_xdisplay); @@ -13719,6 +14260,9 @@ void run_gui(char *gui_xdisplay, int connect_to_x11vnc, pid_t parent) { } } set_env("X11VNC_XDISPLAY", x11vnc_xdisplay); + if (getenv("XAUTHORITY") != NULL) { + set_env("X11VNC_XAUTHORITY", getenv("XAUTHORITY")); + } if (rc == 0) { fprintf(stderr, "gui: ping succeeded.\n"); set_env("X11VNC_CONNECT", "1"); @@ -13726,6 +14270,13 @@ void run_gui(char *gui_xdisplay, int connect_to_x11vnc, pid_t parent) { fprintf(stderr, "gui: could not connect to: '%s', try" " again manually.\n", x11vnc_xdisplay); } + if (dpy) { + XCloseDisplay(dpy); + } + if (old_xauth) { + set_env("XAUTHORITY", old_xauth); + free(old_xauth); + } } orig_path = getenv("PATH"); @@ -13768,6 +14319,9 @@ void run_gui(char *gui_xdisplay, int connect_to_x11vnc, pid_t parent) { set_env("DISPLAY", gui_xdisplay); set_env("X11VNC_PROG", program_name); set_env("X11VNC_CMDLINE", program_cmdline); + if (auth_file) { + set_env("X11VNC_AUTH_FILE", auth_file); + } sprintf(cmd, "%s -", wish); tmpf = tmpfile(); @@ -13871,6 +14425,7 @@ void do_gui(char *opts) { if (! test_dpy) { fprintf(stderr, "error: cannot connect to gui X DISPLAY: %s\n", gui_xdisplay); + exit(1); } XCloseDisplay(test_dpy); @@ -14776,11 +15331,15 @@ static void watch_loop(void) { check_xevents(); check_connect_inputs(); check_padded_fb(); + if (started_as_root) { + check_switched_user(); + } if (first_conn_timeout < 0) { start = time(0); first_conn_timeout = -first_conn_timeout; } + if (! screen || ! screen->clientHead) { /* waiting for a client */ if (first_conn_timeout) { @@ -14788,13 +15347,15 @@ static void watch_loop(void) { rfbLog("No client after %d secs.\n", first_conn_timeout); shut_down = 1; - continue; } } usleep(200 * 1000); continue; } - first_conn_timeout = 0; + + if (first_conn_timeout && all_clients_initialized()) { + first_conn_timeout = 0; + } if (nofb) { /* no framebuffer polling needed */ @@ -14897,7 +15458,8 @@ static void print_help(int mode) { " environment variable to \"disp\".\n" "-auth file Set the X authority file to be \"file\", equivalent to\n" " setting the XAUTHORITY environment variable to \"file\"\n" -" before startup. See Xsecurity(7), xauth(1) man pages.\n" +" before startup. Same as -xauth file. See Xsecurity(7),\n" +" xauth(1) man pages for more info.\n" "\n" "-id windowid Show the window corresponding to \"windowid\" not\n" " the entire display. New windows like popup menus,\n" @@ -14994,10 +15556,13 @@ static void print_help(int mode) { "-connect string For use with \"vncviewer -listen\" reverse connections.\n" " If \"string\" has the form \"host\" or \"host:port\"\n" " the connection is made once at startup. Use commas\n" -" for a list of host's and host:port's. If \"string\"\n" -" contains \"/\" it is instead interpreted as a file to\n" -" periodically check for new hosts. The first line is\n" -" read and then the file is truncated.\n" +" for a list of host's and host:port's.\n" +"\n" +" If \"string\" contains \"/\" it is instead interpreted\n" +" as a file to periodically check for new hosts.\n" +" The first line is read and then the file is truncated.\n" +" Be careful for this usage mode if x11vnc is running as\n" +" root (e.g. via inetd(1) or gdm(1)).\n" "-vncconnect Monitor the VNC_CONNECT X property set by the standard\n" "-novncconnect VNC program vncconnect(1). When the property is\n" " set to \"host\" or \"host:port\" establish a reverse\n" @@ -15035,6 +15600,11 @@ static void print_help(int mode) { " otherwise the client is rejected. See below for an\n" " extension to accept a client view-only.\n" "\n" +" If x11vnc is running as root (say from inetd(1) or from\n" +" display managers xdm(1), gdm(1), etc), think about the\n" +" security implications carefully before supplying this\n" +" option (likewise for the -gone option).\n" +"\n" " Environment: The RFB_CLIENT_IP environment variable will\n" " be set to the incoming client IP number and the port\n" " in RFB_CLIENT_PORT (or -1 if unavailable). Similarly,\n" @@ -15086,6 +15656,53 @@ static void print_help(int mode) { " in -accept. Unlike -accept, the command return code\n" " is not interpreted by x11vnc. Example: -gone 'xlock &'\n" "\n" +"-users list If x11vnc is started as root (say from inetd(1) or\n" +" from display managers xdm(1), gdm(1), etc), then as\n" +" soon as possible after connections to the display are\n" +" established try to switch to one of the users in the\n" +" comma separated \"list\". If x11vnc is not running as\n" +" root this option is ignored.\n" +" \n" +" Why use this option? In general it is not needed\n" +" since x11vnc is already connected to the display and\n" +" can perform its primary functions. It was added to\n" +" make some of the *external* utility commands x11vnc\n" +" occasionally runs work properly. In particular under\n" +" GNOME and KDE to implement the \"-solid color\" feature\n" +" external commands (gconftool-2 and dcop) must be run as\n" +" the user owning the desktop session. This option also\n" +" affects the userid used to run the processes for the\n" +" -accept and -gone options. It also affects the ability\n" +" to read files for options such as -connect, -allow, and\n" +" -remap. Note that the -connect file is also written to.\n" +" \n" +" So be careful with this option since in many situations\n" +" its use can decrease security.\n" +" \n" +" The switch to a user will only take place if the display\n" +" can still be opened as that user (this is primarily to\n" +" try to guess the actual owner of the session). Example:\n" +" \"-users fred,wilma,betty\". Note that a malicious\n" +" user \"barney\" by quickly using \"xhost +\" when\n" +" logging in can get x11vnc to switch to user \"fred\".\n" +" What happens next?\n" +" \n" +" Under display managers it may be a long time before\n" +" the switch succeeds (i.e. a user logs in). To make\n" +" it switch immediately regardless if the display can\n" +" be reopened or not prefix the username with the +\n" +" character. E.g. \"-users +bob\" or \"-users +nobody\".\n" +" The latter is probably the only use of this option\n" +" that increases security. To switch to a user *before*\n" +" connections to the display are made or any files opened\n" +" use the \"=\" character: \"-users =username\".\n" +" \n" +" The special user \"guess\" means to examine the utmpx\n" +" database looking for a user attached to the display\n" +" number and try him/her. To limit the list of guesses,\n" +" use: \"-users guess=bob,fred\". Be especially careful\n" +" using this mode.\n" +" \n" "-noshm Do not use the MIT-SHM extension for the polling.\n" " Remote displays can be polled this way: be careful this\n" " can use large amounts of network bandwidth. This is\n" @@ -15101,15 +15718,18 @@ static void print_help(int mode) { " try to change the desktop background to a solid color.\n" " The [color] is optional: the default color is \"cyan4\".\n" " For a different one specify the X color (rgb.txt name,\n" -" e.g. \"darkblue\" or numerical \"#RRGGBB\"). Currently\n" -" this option only works on GNOME, KDE, and classic X\n" -" (i.e. with the background image on the root window).\n" -" The \"gconftool-2\" and \"dcop\" external commands are\n" -" run for GNOME and KDE respectively. Other desktops\n" -" won't work, e.g. XFCE (send us the corresponding\n" -" commands if you find them). If x11vnc guesses your\n" -" desktop incorrectly, you can force it by prefixing\n" -" color with \"gnome:\", \"kde:\", or \"root:\".\n" +" e.g. \"darkblue\" or numerical \"#RRGGBB\").\n" +"\n" +" Currently this option only works on GNOME, KDE, and\n" +" classic X (i.e. with the background image on the root\n" +" window). The \"gconftool-2\" and \"dcop\" external\n" +" commands are run for GNOME and KDE respectively.\n" +" Other desktops won't work, e.g. XFCE (send us the\n" +" corresponding commands if you find them). If x11vnc\n" +" is running as root (inetd(1) or gdm(1)), the -users\n" +" option may be needed for GNOME and KDE. If x11vnc\n" +" guesses your desktop incorrectly, you can force it by\n" +" prefixing color with \"gnome:\", \"kde:\", or \"root:\".\n" "-blackout string Black out rectangles on the screen. \"string\" is a\n" " comma separated list of WxH+X+Y type geometries for\n" " each rectangle.\n" @@ -15535,8 +16155,7 @@ static void print_help(int mode) { " display (e.g. localhost:10).\n" "\n" " Examples: \"x11vnc -gui\", \"x11vnc -gui localhost:10\",\n" -" \"x11vnc -gui :10\", \"x11vnc -gui wait,:10\",\n" -" \"x11vnc -gui <x11vnc-opts...>\"\n" +" \"x11vnc -gui :10\", \"x11vnc -gui conn,host:10\",\n" "\n" " If you do not specify a gui X display in \"gui-opts\"\n" " then the DISPLAY environment variable and -display\n" @@ -15591,6 +16210,7 @@ static void print_help(int mode) { " \"zero:x1,y1,x2,y2\" for a rectangle.\n" " refresh send the entire fb to all clients.\n" " reset recreate the fb, polling memory, etc.\n" +/* ext. cmd. */ " id:windowid set -id window to \"windowid\". empty\n" " or \"root\" to go back to root window\n" " sid:windowid set -sid window to \"windowid\"\n" @@ -15606,6 +16226,7 @@ static void print_help(int mode) { " visual:vis set -visual to \"vis\"\n" " scale:frac set -scale to \"frac\"\n" " viewonly enable -viewonly mode.\n" +/* access view,share,forever */ " noviewonly disable -viewonly mode.\n" " shared enable -shared mode.\n" " noshared disable -shared mode.\n" @@ -15616,6 +16237,7 @@ static void print_help(int mode) { " connects in the next n secs.\n" " deny deny any new connections, same as \"lock\"\n" " nodeny allow new connections, same as \"unlock\"\n" +/* access, filename */ " connect:host do reverse connection to host, \"host\"\n" " may be a comma separated list of hosts\n" " or host:ports. See -connect.\n" @@ -15625,8 +16247,10 @@ static void print_help(int mode) { " If you know the client internal hex ID,\n" " e.g. 0x3 (returned by -query clients and\n" " RFB_CLIENT_ID), you can use that too.\n" +/* access */ " allowonce:host For the next connection only, allow\n" " connection from \"host\".\n" +/* access */ " allow:hostlist set -allow list to (comma separated)\n" " \"hostlist\". See -allow and -localhost.\n" " Do not use with -allow /path/to/file\n" @@ -15634,6 +16258,7 @@ static void print_help(int mode) { " use \"-host\" to delete a single host\n" " localhost enable -localhost mode\n" " nolocalhost disable -localhost mode\n" +/* ext. cmd. */ " accept:cmd set -accept \"cmd\" (empty to disable).\n" " gone:cmd set -gone \"cmd\" (empty to disable).\n" " noshm enable -noshm mode.\n" @@ -15644,6 +16269,7 @@ static void print_help(int mode) { " onetile enable -onetile mode. (you may need to\n" " set shm for this to do something)\n" " noonetile disable -onetile mode.\n" +/* ext. cmd. */ " solid enable -solid mode\n" " nosolid disable -solid mode.\n" " solid_color:color set -solid color (and apply it).\n" @@ -15674,6 +16300,7 @@ static void print_help(int mode) { " noclear_mods disable -clear_mods mode.\n" " clear_keys enable -clear_keys mode and clear them.\n" " noclear_keys disable -clear_keys mode.\n" +/* filename */ " remap:str set -remap \"str\" (empty to disable).\n" " See -remap for the form of \"str\"\n" " (basically: key1-key2,key3-key4,...)\n" @@ -15733,6 +16360,7 @@ static void print_help(int mode) { " height parameter to n.\n" " desktop:str set -desktop name to str for new clients.\n" " rfbport:n set -rfbport to n.\n" +/* access */ " http enable http client connections.\n" " nohttp disable http client connections.\n" " httpport:n set -httpport to n.\n" @@ -15817,7 +16445,7 @@ static void print_help(int mode) { " nodontdisconnect desktop noremote\n" "\n" " aro= display vncdisplay desktopname http_url auth\n" -" rootshift scale_str scaled_x scaled_y scale_numer\n" +" users rootshift scale_str scaled_x scaled_y scale_numer\n" " scale_denom scale_fac scaling_noblend scaling_nomult4\n" " scaling_pad scaling_interpolate inetd safer unsafe\n" " passwdfile using_shm logfile o rc norc h help V version\n" @@ -16097,17 +16725,22 @@ static void check_rcfile(int argc, char **argv) { perror("fopen"); exit(1); } - } else if (getenv("HOME") == NULL) { - norc = 1; } else { - strncpy(rcfile, getenv("HOME"), 500); - strcat(rcfile, "/.x11vncrc"); - infile = rcfile; - rc = fopen(rcfile, "r"); - if (rc == NULL) { + char *home = get_home_dir(); + if (! home) { norc = 1; } else { - rc_rcfile = strdup(rcfile); + strncpy(rcfile, home, 500); + free(home); + + strcat(rcfile, "/.x11vncrc"); + infile = rcfile; + rc = fopen(rcfile, "r"); + if (rc == NULL) { + norc = 1; + } else { + rc_rcfile = strdup(rcfile); + } } } @@ -16161,12 +16794,7 @@ static void check_rcfile(int argc, char **argv) { if ( (q = strchr(p, '#')) != NULL) { *q = '\0'; } - while (*p) { - if (! isspace(*p)) { - break; - } - p++; - } + p = lblanks(p); strncat(buf, p, sz - strlen(buf) - 1); if (cont) { @@ -16195,12 +16823,7 @@ static void check_rcfile(int argc, char **argv) { p = buf; p += strlen(parm); - while (*p) { - if (! isspace(*p)) { - break; - } - p++; - } + p = lblanks(p); if (*p == '\0') { buf[0] = '\0'; continue; @@ -16258,6 +16881,45 @@ int main(int argc, char* argv[]) { /* if we are root limit some remote commands: */ if (!getuid() || !geteuid()) { safe_remote_only = 1; + started_as_root = 1; + + /* check for '-users =fred' */ + for (i=1; i < argc; i++) { + char *u; + int saved; + + if (strcmp(argv[i], "-users")) { + continue; + } + if (i == argc - 1) { + fprintf(stderr, "not enough arguments for: " + "-users\n"); + exit(1); + } + if (*(argv[i+1]) != '=') { + break; + } + u = strdup(argv[i+1]); + *u = '+'; + if (strstr(u, "+guess") == u) { + fprintf(stderr, "invalid user: %s\n", u); + exit(1); + } + /* kludge... */ + saved = using_shm; + using_shm = 0; + if (!switch_user(u)) { + fprintf(stderr, "Could not switch to user: " + "%s\n", u+1); + exit(1); + } else { + fprintf(stderr, "Switched to user: %s\n", u+1); + started_as_root = 2; + } + using_shm = saved; + free(u); + break; + } } argv_vnc[0] = strdup(argv[0]); @@ -16305,7 +16967,7 @@ int main(int argc, char* argv[]) { if (!strcmp(arg, "-display")) { CHECK_ARGC use_dpy = strdup(argv[++i]); - } else if (!strcmp(arg, "-auth")) { + } else if (!strcmp(arg, "-auth") || !strcmp(arg, "-xauth")) { CHECK_ARGC auth_file = strdup(argv[++i]); } else if (!strcmp(arg, "-id") || !strcmp(arg, "-sid")) { @@ -16361,6 +17023,9 @@ int main(int argc, char* argv[]) { } else if (!strcmp(arg, "-timeout")) { CHECK_ARGC first_conn_timeout = atoi(argv[++i]); + } else if (!strcmp(arg, "-users")) { + CHECK_ARGC + users_list = strdup(argv[++i]); } else if (!strcmp(arg, "-inetd")) { inetd = 1; } else if (!strcmp(arg, "-connect")) { @@ -16927,6 +17592,8 @@ int main(int argc, char* argv[]) { : "null"); fprintf(stderr, " gone: %s\n", gone_cmd ? gone_cmd : "null"); + fprintf(stderr, " users: %s\n", users_list ? users_list + : "null"); fprintf(stderr, " using_shm: %d\n", using_shm); fprintf(stderr, " flipbytes: %d\n", flip_byte_order); fprintf(stderr, " onetile: %d\n", single_copytile); @@ -17162,7 +17829,8 @@ int main(int argc, char* argv[]) { " cursor mode\n"); rfbLog(" to: '-cursor most'.\n"); rfbLog(" to disable this behavior use: " - "'-cursor arrow'.\n"); + "'-cursor arrow'\n"); + rfbLog(" or '-noxfixes'.\n"); } } if(!strcmp(multiple_cursors_mode, "most")) { |