/* -- user.c -- */ #include "x11vnc.h" #include "solid.h" #include "cleanup.h" #include "scan.h" #include "screen.h" #include "unixpw.h" #include "sslhelper.h" #include "xwrappers.h" #include "connections.h" #include "inet.h" #include "keyboard.h" #include "cursor.h" #include "remote.h" #include "sslhelper.h" #include "avahi.h" void check_switched_user(void); void lurk_loop(char *str); int switch_user(char *user, int fb_mode); int read_passwds(char *passfile); void install_passwds(void); void check_new_passwds(int force); void progress_client(void); int wait_for_client(int *argc, char** argv, int http); rfbBool custom_passwd_check(rfbClientPtr cl, const char *response, int len); char *xdmcp_insert = NULL; static void switch_user_task_dummy(void); static void switch_user_task_solid_bg(void); static char *get_login_list(int with_display); static char **user_list(char *user_str); static void user2uid(char *user, uid_t *uid, gid_t *gid, char **name, char **home); static int lurk(char **users); static int guess_user_and_switch(char *str, int fb_mode); static int try_user_and_display(uid_t uid, gid_t gid, char *dpystr); static int switch_user_env(uid_t uid, gid_t gid, char *name, char *home, int fb_mode); static void try_to_switch_users(void); /* tasks for after we switch */ static void switch_user_task_dummy(void) { ; /* dummy does nothing */ } static void switch_user_task_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(NULL); if (unixpw_in_progress) return; 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_task_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_task_solid_bg(); did_solid = 1; } } if (did_dummy && did_solid) { sched_switched_user = 0; } } /* utilities for switching users */ static char *get_login_list(int with_display) { char *out; #if LIBVNCSERVER_HAVE_UTMPX_H int i, cnt, max = 200, ut_namesize = 32; int dpymax = 1000, sawdpy[1000]; struct utmpx *utx; /* size based on "username:999," * max */ out = (char *) malloc(max * (ut_namesize+1+3+1) + 1); out[0] = '\0'; for (i=0; iut_type != USER_PROCESS) { continue; } user = lblanks(utx->ut_user); if (*user == '\0') { continue; } if (strchr(user, ',')) { continue; /* unlikely, but comma is our sep. */ } line = lblanks(utx->ut_line); host = lblanks(utx->ut_host); id = lblanks(utx->ut_id); if (with_display) { if (0 && line[0] != ':' && strcmp(line, "dtlocal")) { /* XXX useful? */ continue; } if (line[0] == ':') { if (sscanf(line, ":%d", &d) != 1) { d = -1; } } if (d < 0 && host[0] == ':') { if (sscanf(host, ":%d", &d) != 1) { d = -1; } } if (d < 0 && id[0] == ':') { if (sscanf(id, ":%d", &d) != 1) { d = -1; } } if (d < 0 || d >= dpymax || sawdpy[d]) { continue; } sawdpy[d] = 1; sprintf(tmp, ":%d", d); } else { /* try to eliminate repeats */ int repeat = 0; char *q; q = out; while ((q = strstr(q, user)) != NULL) { char *p = q + strlen(user) + strlen(":DPY"); if (q == out || *(q-1) == ',') { /* bounded on left. */ if (*p == ',' || *p == '\0') { /* bounded on right. */ repeat = 1; break; } } q = p; } if (repeat) { continue; } sprintf(tmp, ":DPY"); } if (*out) { strcat(out, ","); } strcat(out, user); strcat(out, tmp); cnt++; if (cnt >= max) { break; } } endutxent(); #else out = strdup(""); #endif return out; } static char **user_list(char *user_str) { int n, i; char *p, **list; p = user_str; n = 1; while (*p++) { if (*p == ',') { n++; } } list = (char **) calloc((n+1)*sizeof(char *), 1); p = strtok(user_str, ","); i = 0; while (p) { list[i++] = strdup(p); p = strtok(NULL, ","); } list[i] = NULL; return list; } static void user2uid(char *user, uid_t *uid, gid_t *gid, char **name, char **home) { int numerical = 1, gotgroup = 0; char *q; *uid = (uid_t) -1; *name = NULL; *home = NULL; q = user; while (*q) { if (! isdigit((unsigned char) (*q++))) { numerical = 0; break; } } if (user2group != NULL) { static int *did = NULL; int i; if (did == NULL) { int n = 0; i = 0; while (user2group[i] != NULL) { n++; i++; } did = (int *) malloc((n+1) * sizeof(int)); i = 0; for (i=0; igr_gid; if (! did[i]) { rfbLog("user2uid: using group %s (%d) for %s\n", w, (int) *gid, user); did[i] = 1; } gotgroup = 1; } } i++; } } if (numerical) { int u = atoi(user); if (u < 0) { return; } *uid = (uid_t) u; } #if LIBVNCSERVER_HAVE_PWD_H if (1) { struct passwd *pw; if (numerical) { pw = getpwuid(*uid); } else { pw = getpwnam(user); } if (pw) { *uid = pw->pw_uid; if (! gotgroup) { *gid = pw->pw_gid; } *name = pw->pw_name; /* n.b. use immediately */ *home = pw->pw_dir; } } #endif } static int lurk(char **users) { uid_t uid; gid_t gid; int success = 0, dmin = -1, dmax = -1; char *p, *logins, **u; char **list; int lind; if ((u = users) != NULL && *u != NULL && *(*u) == ':') { int len; char *tmp; /* extract min and max display numbers */ tmp = *u; if (strchr(tmp, '-')) { if (sscanf(tmp, ":%d-%d", &dmin, &dmax) != 2) { dmin = -1; dmax = -1; } } if (dmin < 0) { if (sscanf(tmp, ":%d", &dmin) != 1) { dmin = -1; dmax = -1; } else { dmax = dmin; } } if ((dmin < 0 || dmax < 0) || dmin > dmax || dmax > 10000) { dmin = -1; dmax = -1; } /* get user logins regardless of having a display: */ logins = get_login_list(0); /* * now we append the list in users (might as well try * them) this will probably allow weird ways of starting * xservers to work. */ len = strlen(logins); u++; while (*u != NULL) { len += strlen(*u) + strlen(":DPY,"); u++; } tmp = (char *) malloc(len+1); strcpy(tmp, logins); /* now concatenate them: */ u = users+1; while (*u != NULL) { char *q, chk[100]; snprintf(chk, 100, "%s:DPY", *u); q = strstr(tmp, chk); if (q) { char *p = q + strlen(chk); if (q == tmp || *(q-1) == ',') { /* bounded on left. */ if (*p == ',' || *p == '\0') { /* bounded on right. */ u++; continue; } } } if (*tmp) { strcat(tmp, ","); } strcat(tmp, *u); strcat(tmp, ":DPY"); u++; } free(logins); logins = tmp; } else { logins = get_login_list(1); } list = (char **) calloc((strlen(logins)+2)*sizeof(char *), 1); lind = 0; p = strtok(logins, ","); while (p) { list[lind++] = strdup(p); p = strtok(NULL, ","); } free(logins); lind = 0; while (list[lind] != NULL) { char *user, *name, *home, dpystr[10]; char *q, *t; int ok = 1, dn; p = list[lind++]; t = strdup(p); /* bob:0 */ q = strchr(t, ':'); if (! q) { free(t); break; } *q = '\0'; user = t; snprintf(dpystr, 10, ":%s", q+1); if (users) { u = users; ok = 0; while (*u != NULL) { if (*(*u) == ':') { u++; continue; } if (!strcmp(user, *u++)) { ok = 1; break; } } } user2uid(user, &uid, &gid, &name, &home); free(t); if (! uid || ! gid) { ok = 0; } if (! ok) { continue; } for (dn = dmin; dn <= dmax; dn++) { if (dn >= 0) { sprintf(dpystr, ":%d", dn); } if (try_user_and_display(uid, gid, dpystr)) { if (switch_user_env(uid, gid, name, home, 0)) { rfbLog("lurk: now user: %s @ %s\n", name, dpystr); started_as_root = 2; success = 1; } set_env("DISPLAY", dpystr); break; } } if (success) { break; } } lind = 0; while (list[lind] != NULL) { free(list[lind]); lind++; } return success; } void lurk_loop(char *str) { char *tstr = NULL, **users = NULL; if (strstr(str, "lurk=") != str) { exit(1); } rfbLog("lurking for logins using: '%s'\n", str); if (strlen(str) > strlen("lurk=")) { char *q = strchr(str, '='); tstr = strdup(q+1); users = user_list(tstr); } while (1) { if (lurk(users)) { break; } sleep(3); } if (tstr) { free(tstr); } if (users) { free(users); } } static int guess_user_and_switch(char *str, int fb_mode) { char *dstr, *d; char *p, *tstr = NULL, *allowed = NULL, *logins, **users = NULL; int dpy1, ret = 0; char **list; int lind; RAWFB_RET(0) d = DisplayString(dpy); /* pick out ":N" */ dstr = strchr(d, ':'); if (! dstr) { return 0; } if (sscanf(dstr, ":%d", &dpy1) != 1) { return 0; } if (dpy1 < 0) { return 0; } if (strstr(str, "guess=") == str && strlen(str) > strlen("guess=")) { allowed = strchr(str, '='); allowed++; tstr = strdup(allowed); users = user_list(tstr); } /* loop over the utmpx entries looking for this display */ logins = get_login_list(1); list = (char **) calloc((strlen(logins)+2)*sizeof(char *), 1); lind = 0; p = strtok(logins, ","); while (p) { list[lind++] = strdup(p); p = strtok(NULL, ","); } lind = 0; while (list[lind] != NULL) { char *user, *q, *t; int dpy2, ok = 1; p = list[lind++]; t = strdup(p); q = strchr(t, ':'); if (! q) { free(t); break; } *q = '\0'; user = t; dpy2 = atoi(q+1); if (users) { char **u = users; ok = 0; while (*u != NULL) { if (!strcmp(user, *u++)) { ok = 1; break; } } } if (dpy1 != dpy2) { ok = 0; } if (! ok) { free(t); continue; } if (switch_user(user, fb_mode)) { rfbLog("switched to guessed user: %s\n", user); free(t); ret = 1; break; } } if (tstr) { free(tstr); } if (users) { free(users); } if (logins) { free(logins); } return ret; } static int try_user_and_display(uid_t uid, gid_t gid, char *dpystr) { /* NO strtoks */ #if LIBVNCSERVER_HAVE_FORK && LIBVNCSERVER_HAVE_SYS_WAIT_H && LIBVNCSERVER_HAVE_PWD_H pid_t pid, pidw; char *home, *name; int st; struct passwd *pw; pw = getpwuid(uid); if (pw) { name = pw->pw_name; home = pw->pw_dir; } else { return 0; } /* * 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 { /* child */ Display *dpy2 = NULL; int rc; signal(SIGHUP, SIG_DFL); signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGTERM, SIG_DFL); rc = switch_user_env(uid, gid, name, home, 0); if (! rc) { exit(1); } fclose(stderr); dpy2 = XOpenDisplay_wr(dpystr); if (dpy2) { XCloseDisplay_wr(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) { return 1; } #endif /* LIBVNCSERVER_HAVE_FORK ... */ return 0; } int switch_user(char *user, int fb_mode) { /* NO strtoks */ int doit = 0; uid_t uid = 0; gid_t gid = 0; char *name, *home; if (*user == '+') { doit = 1; user++; } ssl_helper_pid(0, -2); /* waitall */ if (strstr(user, "guess=") == user) { return guess_user_and_switch(user, fb_mode); } user2uid(user, &uid, &gid, &name, &home); if (uid == (uid_t) -1 || uid == 0) { return 0; } if (gid == 0) { return 0; } if (! doit && dpy) { /* see if this display works: */ char *dstr = DisplayString(dpy); doit = try_user_and_display(uid, gid, dstr); } if (doit) { int rc = switch_user_env(uid, gid, name, home, fb_mode); if (rc) { started_as_root = 2; } return rc; } else { return 0; } } static int switch_user_env(uid_t uid, gid_t gid, char *name, char *home, int fb_mode) { /* NO strtoks */ char *xauth; int reset_fb = 0; int grp_ok = 0; #if !LIBVNCSERVER_HAVE_SETUID return 0; #else /* * OK tricky here, we need to free the shm... otherwise * we won't be able to delete it as the other user... */ if (fb_mode == 1 && (using_shm && ! xform24to32)) { reset_fb = 1; clean_shm(0); free_tiles(); } #if LIBVNCSERVER_HAVE_INITGROUPS #if LIBVNCSERVER_HAVE_PWD_H if (getpwuid(uid) != NULL && getenv("X11VNC_SINGLE_GROUP") == NULL) { struct passwd *p = getpwuid(uid); if (initgroups(p->pw_name, gid) == 0) { grp_ok = 1; } else { rfbLogPerror("initgroups"); } } #endif #endif if (! grp_ok) { if (setgid(gid) == 0) { grp_ok = 1; } } if (! grp_ok) { if (reset_fb) { /* 2 means we did clean_shm and free_tiles */ do_new_fb(2); } return 0; } if (setuid(uid) != 0) { if (reset_fb) { /* 2 means we did clean_shm and free_tiles */ do_new_fb(2); } return 0; } #endif if (reset_fb) { do_new_fb(2); } xauth = getenv("XAUTHORITY"); if (xauth && access(xauth, R_OK) != 0) { *(xauth-2) = '_'; /* yow */ } set_env("USER", name); set_env("LOGNAME", name); set_env("HOME", home); return 1; } static void try_to_switch_users(void) { static time_t last_try = 0; time_t now = time(NULL); 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, 1)) { started_as_root = 2; } free(users); return; } p = strtok(users, ","); while (p) { if (switch_user(p, 1)) { started_as_root = 2; rfbLog("try_to_switch_users: now %s\n", p); break; } p = strtok(NULL, ","); } free(users); } int read_passwds(char *passfile) { char line[1024]; char *filename; char **old_passwd_list = passwd_list; int linecount = 0, i, remove = 0, read_mode = 0, begin_vo = -1; struct stat sbuf; static int max = -1; FILE *in = NULL; static time_t last_read = 0; static int read_cnt = 0; int db_passwd = 0; if (max < 0) { max = 1000; if (getenv("X11VNC_MAX_PASSWDS")) { max = atoi(getenv("X11VNC_MAX_PASSWDS")); } } filename = passfile; if (strstr(filename, "rm:") == filename) { filename += strlen("rm:"); remove = 1; } else if (strstr(filename, "read:") == filename) { filename += strlen("read:"); read_mode = 1; if (stat(filename, &sbuf) == 0) { if (sbuf.st_mtime <= last_read) { return 1; } last_read = sbuf.st_mtime; } } else if (strstr(filename, "cmd:") == filename) { int rc; filename += strlen("cmd:"); read_mode = 1; in = tmpfile(); if (in == NULL) { rfbLog("run_user_command tmpfile() failed: %s\n", filename); clean_up_exit(1); } rc = run_user_command(filename, latest_client, "read_passwds", NULL, 0, in); if (rc != 0) { rfbLog("run_user_command command failed: %s\n", filename); clean_up_exit(1); } rewind(in); } else if (strstr(filename, "custom:") == filename) { return 1; } if (in == NULL && stat(filename, &sbuf) == 0) { /* (poor...) upper bound to number of lines */ max = (int) sbuf.st_size; last_read = sbuf.st_mtime; } /* create 1 more than max to have it be the ending NULL */ passwd_list = (char **) malloc( (max+1) * (sizeof(char *)) ); for (i=0; i= max) { rfbLog("read_passwds: hit max passwd: %d\n", max); break; } } fclose(in); for (i=0; i<1024; i++) { line[i] = '\0'; } if (remove) { unlink(filename); } if (! linecount) { rfbLog("cannot read a valid line from passwdfile: %s\n", passfile); if (read_cnt == 0) { clean_up_exit(1); } else { return 0; } } read_cnt++; for (i=0; i 1) { if (viewonly_passwd) { free(viewonly_passwd); viewonly_passwd = NULL; } } if (begin_viewonly < 0 && linecount == 2) { /* for compatibility with previous 2-line usage: */ viewonly_passwd = strdup(passwd_list[1]); if (db_passwd) { fprintf(stderr, "read_passwds: linecount is 2.\n"); } if (screen) { char **apd = (char **) screen->authPasswdData; if (apd) { if (apd[0] != NULL) { strzero(apd[0]); } apd[0] = strdup(passwd_list[0]); } } begin_viewonly = 1; } if (old_passwd_list != NULL) { char *p; i = 0; while (old_passwd_list[i] != NULL) { p = old_passwd_list[i]; strzero(p); free(old_passwd_list[i]); i++; } free(old_passwd_list); } return 1; } void install_passwds(void) { if (viewonly_passwd) { /* append the view only passwd after the normal passwd */ char **passwds_new = (char **) malloc(3*sizeof(char *)); char **passwds_old = (char **) screen->authPasswdData; passwds_new[0] = passwds_old[0]; passwds_new[1] = viewonly_passwd; passwds_new[2] = NULL; screen->authPasswdData = (void*) passwds_new; } else if (passwd_list) { int i = 0; while(passwd_list[i] != NULL) { i++; } if (begin_viewonly < 0) { begin_viewonly = i+1; } screen->authPasswdData = (void*) passwd_list; screen->authPasswdFirstViewOnly = begin_viewonly; } } void check_new_passwds(int force) { static time_t last_check = 0; time_t now; if (! passwdfile) { return; } if (strstr(passwdfile, "read:") != passwdfile) { return; } if (unixpw_in_progress) return; if (force) { last_check = 0; } now = time(NULL); if (now > last_check + 1) { if (read_passwds(passwdfile)) { install_passwds(); } last_check = now; } } rfbBool custom_passwd_check(rfbClientPtr cl, const char *response, int len) { char *input, *cmd; char num[16]; int j, i, n, rc; rfbLog("custom_passwd_check: len=%d\n", len); if (!passwdfile || strstr(passwdfile, "custom:") != passwdfile) { return FALSE; } cmd = passwdfile + strlen("custom:"); sprintf(num, "%d\n", len); input = (char *) malloc(2 * len + 16 + 1); input[0] = '\0'; strcat(input, num); n = strlen(num); j = n; for (i=0; i < len; i++) { input[j] = cl->authChallenge[i]; j++; } for (i=0; i < len; i++) { input[j] = response[i]; j++; } rc = run_user_command(cmd, cl, "custom_passwd", input, n+2*len, NULL); free(input); if (rc == 0) { return TRUE; } else { return FALSE; } } static void handle_one_http_request(void) { rfbLog("handle_one_http_request: begin.\n"); if (inetd || screen->httpPort == 0) { int port = find_free_port(5800, 5860); if (port) { screen->httpPort = port; } else { rfbLog("handle_one_http_request: no http port.\n"); clean_up_exit(1); } } screen->autoPort = FALSE; screen->port = 0; http_connections(1); rfbInitServer(screen); if (!inetd) { int conn = 0; while (1) { if (0) fprintf(stderr, "%d %d %d %d\n", conn, screen->listenSock, screen->httpSock, screen->httpListenSock); usleep(10 * 1000); rfbHttpCheckFds(screen); if (conn) { if (screen->httpSock < 0) { break; } } else { if (screen->httpSock >= 0) { conn = 1; } } if (!screen->httpDir) { break; } if (screen->httpListenSock < 0) { break; } } rfbLog("handle_one_http_request: finished.\n"); return; } else { /* inetd case: */ #if LIBVNCSERVER_HAVE_FORK pid_t pid; int s_in = screen->inetdSock; if (s_in < 0) { rfbLog("handle_one_http_request: inetdSock not set up.\n"); clean_up_exit(1); } pid = fork(); if (pid < 0) { rfbLog("handle_one_http_request: could not fork.\n"); clean_up_exit(1); } else if (pid > 0) { int status; pid_t pidw; while (1) { rfbHttpCheckFds(screen); pidw = waitpid(pid, &status, WNOHANG); if (pidw == pid && WIFEXITED(status)) { break; } else if (pidw < 0) { break; } } rfbLog("handle_one_http_request: finished.\n"); return; } else { int sock = rfbConnectToTcpAddr("127.0.0.1", screen->httpPort); if (sock < 0) { exit(1); } raw_xfer(sock, s_in, s_in); exit(0); } #else rfbLog("handle_one_http_request: fork not supported.\n"); clean_up_exit(1); #endif } } void user_supplied_opts(char *opts) { char *p, *str; char *allow[] = { "skip-display", "skip-auth", "skip-shared", "scale", "scale_cursor", "sc", "solid", "so", "id", "clear_mods", "cm", "clear_keys", "ck", "repeat", "clear_all", "ca", "speeds", "sp", "readtimeout", "rd", "rotate", "ro", "geometry", "geom", "ge", "noncache", "nc", "nodisplay", "nd", "viewonly", "vo", NULL }; if (getenv("X11VNC_NO_UNIXPW_OPTS")) { return; } str = strdup(opts); p = strtok(str, ","); while (p) { char *q; int i, n, m, ok = 0; i = 0; while (allow[i] != NULL) { if (strstr(allow[i], "skip-")) { i++; continue; } if (strstr(p, allow[i]) == p) { ok = 1; break; } i++; } if (! ok && strpbrk(p, "0123456789") == p && sscanf(p, "%d/%d", &n, &m) == 2) { if (scale_str) free(scale_str); scale_str = strdup(p); } else if (ok) { if (strstr(p, "display=") == p) { if (use_dpy) free(use_dpy); use_dpy = strdup(p + strlen("display=")); } else if (strstr(p, "auth=") == p) { if (auth_file) free(auth_file); auth_file = strdup(p + strlen("auth=")); } else if (!strcmp(p, "shared")) { shared = 1; } else if (strstr(p, "scale=") == p) { if (scale_str) free(scale_str); scale_str = strdup(p + strlen("scale=")); } else if (strstr(p, "scale_cursor=") == p || strstr(p, "sc=") == p) { if (scale_cursor_str) free(scale_cursor_str); q = strchr(p, '=') + 1; scale_cursor_str = strdup(q); } else if (strstr(p, "rotate=") == p || strstr(p, "ro=") == p) { if (rotating_str) free(rotating_str); q = strchr(p, '=') + 1; rotating_str = strdup(q); } else if (!strcmp(p, "solid") || !strcmp(p, "so")) { use_solid_bg = 1; if (!solid_str) { solid_str = strdup(solid_default); } } else if (!strcmp(p, "viewonly") || !strcmp(p, "vo")) { view_only = 1; } else if (strstr(p, "solid=") == p || strstr(p, "so=") == p) { use_solid_bg = 1; if (solid_str) free(solid_str); q = strchr(p, '=') + 1; if (!strcmp(q, "R")) { solid_str = strdup("root:"); } else { solid_str = strdup(q); } } else if (strstr(p, "id=") == p) { unsigned long win; q = p + strlen("id="); if (strcmp(q, "pick")) { if (scan_hexdec(q, &win)) { subwin = win; } } } else if (!strcmp(p, "clear_mods") || !strcmp(p, "cm")) { clear_mods = 1; } else if (!strcmp(p, "clear_keys") || !strcmp(p, "ck")) { clear_mods = 2; } else if (!strcmp(p, "clear_all") || !strcmp(p, "ca")) { clear_mods = 3; } else if (!strcmp(p, "noncache") || !strcmp(p, "nc")) { ncache = 0; ncache0 = 0; } else if (strstr(p, "nc=") == p) { int n2 = atoi(p + strlen("nc=")); if (nabs(n2) < nabs(ncache)) { if (ncache < 0) { ncache = -nabs(n2); } else { ncache = nabs(n2); } } } else if (!strcmp(p, "repeat")) { no_autorepeat = 0; } else if (strstr(p, "speeds=") == p || strstr(p, "sp=") == p) { if (speeds_str) free(speeds_str); q = strchr(p, '=') + 1; speeds_str = strdup(q); q = speeds_str; while (*q != '\0') { if (*q == '-') { *q = ','; } q++; } } else if (strstr(p, "readtimeout=") == p || strstr(p, "rd=") == p) { q = strchr(p, '=') + 1; rfbMaxClientWait = atoi(q) * 1000; } } else { rfbLog("skipping option: %s\n", p); } p = strtok(NULL, ","); } free(str); } static void vnc_redirect_timeout (int sig) { write(2, "timeout: no clients connected.\n", 31); if (sig) {}; exit(0); } static void do_chvt(int vt) { char chvt[100]; sprintf(chvt, "chvt %d >/dev/null 2>/dev/null &", vt); rfbLog("running: %s\n", chvt); system(chvt); sleep(2); } static void setup_fake_fb(XImage* fb_image, int w, int h, int b) { if (fake_fb) { free(fake_fb); } fake_fb = (char *) calloc(w*h*b/8, 1); fb_image->data = fake_fb; fb_image->format = ZPixmap; fb_image->width = w; fb_image->height = h; fb_image->bits_per_pixel = b; fb_image->bytes_per_line = w*b/8; fb_image->bitmap_unit = -1; if (b >= 24) { fb_image->depth = 24; fb_image->red_mask = 0xff0000; fb_image->green_mask = 0x00ff00; fb_image->blue_mask = 0x0000ff; } else if (b >= 16) { fb_image->depth = 16; fb_image->red_mask = 0x003f; fb_image->green_mask = 0x07c0; fb_image->blue_mask = 0xf800; } else if (b >= 2) { fb_image->depth = 8; fb_image->red_mask = 0x07; fb_image->green_mask = 0x38; fb_image->blue_mask = 0xc0; } else { fb_image->depth = 1; fb_image->red_mask = 0x1; fb_image->green_mask = 0x1; fb_image->blue_mask = 0x1; } depth = fb_image->depth; dpy_x = wdpy_x = w; dpy_y = wdpy_y = h; off_x = 0; off_y = 0; } static void setup_service(void) { if (!inetd) { if (!use_openssl) { announce(screen->port, use_openssl, NULL); fprintf(stdout, "PORT=%d\n", screen->port); } else { fprintf(stdout, "PORT=%d\n", screen->port); if (stunnel_port) { fprintf(stdout, "SSLPORT=%d\n", stunnel_port); } else if (use_openssl) { fprintf(stdout, "SSLPORT=%d\n", screen->port); } } fflush(stdout); } else if (!use_openssl && avahi) { char *name = rfb_desktop_name; if (!name) { name = use_dpy; } avahi_initialise(); avahi_advertise(name, this_host(), screen->port); } } static void check_waitbg(void) { if (getenv("WAITBG")) { #if LIBVNCSERVER_HAVE_FORK && LIBVNCSERVER_HAVE_SETSID int p, n; if ((p = fork()) > 0) { exit(0); } else if (p == -1) { rfbLogEnable(1); fprintf(stderr, "could not fork\n"); perror("fork"); clean_up_exit(1); } if (setsid() == -1) { rfbLogEnable(1); fprintf(stderr, "setsid failed\n"); perror("setsid"); clean_up_exit(1); } /* adjust our stdio */ n = open("/dev/null", O_RDONLY); dup2(n, 0); dup2(n, 1); if (! logfile) { dup2(n, 2); } if (n > 2) { close(n); } #else clean_up_exit(1); #endif } } static void setup_client_connect(int *did_client_connect) { if (client_connect != NULL) { char *remainder = NULL; if (inetd) { rfbLog("wait_for_client: -connect disallowed in inetd mode: %s\n", client_connect); } else if (screen && screen->clientHead) { rfbLog("wait_for_client: -connect disallowed: client exists: %s\n", client_connect); } else if (strchr(client_connect, '=')) { rfbLog("wait_for_client: invalid -connect string: %s\n", client_connect); } else { char *q = strchr(client_connect, ','); if (q) { rfbLog("wait_for_client: only using first" " connect host in: %s\n", client_connect); remainder = strdup(q+1); *q = '\0'; } rfbLog("wait_for_client: reverse_connect(%s)\n", client_connect); reverse_connect(client_connect); *did_client_connect = 1; } free(client_connect); if (remainder != NULL) { /* reset to host2,host3,... */ client_connect = remainder; } else { client_connect = NULL; } } } static void loop_for_connect(int did_client_connect) { int loop = 0; time_t start = time(NULL); if (first_conn_timeout < 0) { first_conn_timeout = -first_conn_timeout; } while (1) { loop++; if (first_conn_timeout && time(NULL) > start + first_conn_timeout) { rfbLog("no client connect after %d seconds.\n", first_conn_timeout); shut_down = 1; } if (shut_down) { clean_up_exit(0); } if (loop < 2) { if (did_client_connect) { goto screen_check; } if (inetd) { goto screen_check; } if (screen && screen->clientHead) { goto screen_check; } } if (use_openssl && !inetd) { check_openssl(); check_https(); /* * This is to handle an initial verify cert from viewer, * they disconnect right after fetching the cert. */ if (! use_threads) rfbPE(-1); if (screen && screen->clientHead) { int i; if (unixpw) { if (! unixpw_in_progress) { rfbLog("unixpw but no unixpw_in_progress\n"); clean_up_exit(1); } if (unixpw_client && unixpw_client->onHold) { rfbLog("taking unixpw_client off hold\n"); unixpw_client->onHold = FALSE; } } for (i=0; i<10; i++) { if (shut_down) { clean_up_exit(0); } usleep(20 * 1000); if (0) rfbLog("wait_for_client: %d\n", i); if (! use_threads) { if (unixpw) { unixpw_in_rfbPE = 1; } rfbPE(-1); if (unixpw) { unixpw_in_rfbPE = 0; } } if (unixpw && !unixpw_in_progress) { /* XXX too soon. */ goto screen_check; } if (!screen->clientHead) { break; } } } } else if (use_openssl) { check_openssl(); } if (! use_threads) { rfbPE(-1); } screen_check: if (! screen || ! screen->clientHead) { usleep(100 * 1000); continue; } rfbLog("wait_for_client: got client\n"); break; } } static void do_unixpw_loop(void) { if (unixpw) { if (! unixpw_in_progress) { rfbLog("unixpw but no unixpw_in_progress\n"); clean_up_exit(1); } if (unixpw_client && unixpw_client->onHold) { rfbLog("taking unixpw_client off hold.\n"); unixpw_client->onHold = FALSE; } while (1) { if (shut_down) { clean_up_exit(0); } if (! use_threads) { unixpw_in_rfbPE = 1; rfbPE(-1); unixpw_in_rfbPE = 0; } if (unixpw_in_progress) { usleep(20 * 1000); continue; } rfbLog("wait_for_client: unixpw finished.\n"); break; } } } static void vnc_redirect_loop(char *vnc_redirect_test, int *vnc_redirect_cnt) { if (unixpw) { rfbLog("wait_for_client: -unixpw and Xvnc.redirect not allowed\n"); clean_up_exit(1); } if (client_connect) { rfbLog("wait_for_client: -connect and Xvnc.redirect not allowed\n"); clean_up_exit(1); } if (inetd) { if (use_openssl) { accept_openssl(OPENSSL_INETD, -1); } } else { pid_t pid = 0; if (screen->httpListenSock >= 0) { #if LIBVNCSERVER_HAVE_FORK if ((pid = fork()) > 0) { close(screen->httpListenSock); screen->httpListenSock = -2; usleep(500 * 1000); } else { close(screen->listenSock); screen->listenSock = -1; while (1) { usleep(10 * 1000); rfbHttpCheckFds(screen); } exit(1); } #else clean_up_exit(1); #endif } if (first_conn_timeout) { if (first_conn_timeout < 0) { first_conn_timeout = -first_conn_timeout; } signal(SIGALRM, vnc_redirect_timeout); alarm(first_conn_timeout); } if (use_openssl) { int i; if (pid == 0) { accept_openssl(OPENSSL_VNC, -1); } else { for (i=0; i < 16; i++) { accept_openssl(OPENSSL_VNC, -1); rfbLog("iter %d: vnc_redirect_sock: %d\n", i, vnc_redirect_sock); if (vnc_redirect_sock >= 0) { break; } } } } else { struct sockaddr_in addr; #ifdef __hpux int addrlen = sizeof(addr); #else socklen_t addrlen = sizeof(addr); #endif if (screen->listenSock < 0) { rfbLog("wait_for_client: Xvnc.redirect not listening... sock=%d port=%d\n", screen->listenSock, screen->port); clean_up_exit(1); } vnc_redirect_sock = accept(screen->listenSock, (struct sockaddr *)&addr, &addrlen); } if (first_conn_timeout) { alarm(0); } if (pid > 0) { #if LIBVNCSERVER_HAVE_FORK int rc; pid_t pidw; rfbLog("wait_for_client: kill TERM: %d\n", (int) pid); kill(pid, SIGTERM); usleep(1000 * 1000); /* 1.0 sec */ pidw = waitpid(pid, &rc, WNOHANG); if (pidw <= 0) { usleep(1000 * 1000); /* 1.0 sec */ pidw = waitpid(pid, &rc, WNOHANG); } #else clean_up_exit(1); #endif } } if (vnc_redirect_sock < 0) { rfbLog("wait_for_client: vnc_redirect failed.\n"); clean_up_exit(1); } if (!inetd && use_openssl) { /* check for Fetch Cert closing */ fd_set rfds; struct timeval tv; int nfds; usleep(300*1000); FD_ZERO(&rfds); FD_SET(vnc_redirect_sock, &rfds); tv.tv_sec = 0; tv.tv_usec = 200000; nfds = select(vnc_redirect_sock+1, &rfds, NULL, NULL, &tv); rfbLog("wait_for_client: vnc_redirect nfds: %d\n", nfds); if (nfds > 0) { int n; n = read(vnc_redirect_sock, vnc_redirect_test, 1); if (n <= 0) { close(vnc_redirect_sock); vnc_redirect_sock = -1; rfbLog("wait_for_client: waiting for 2nd connection (Fetch Cert?)\n"); accept_openssl(OPENSSL_VNC, -1); if (vnc_redirect_sock < 0) { rfbLog("wait_for_client: vnc_redirect failed.\n"); clean_up_exit(1); } } else { *vnc_redirect_cnt = n; } } } } static void do_vnc_redirect(int created_disp, char *vnc_redirect_host, int vnc_redirect_port, int vnc_redirect_cnt, char *vnc_redirect_test) { char *q = strchr(use_dpy, ':'); int vdpy = -1, sock = -1; int s_in, s_out, i; if (vnc_redirect == 2) { char num[32]; sprintf(num, ":%d", vnc_redirect_port); q = num; } if (!q) { rfbLog("wait_for_client: can't find number in X display: %s\n", use_dpy); clean_up_exit(1); } if (sscanf(q+1, "%d", &vdpy) != 1) { rfbLog("wait_for_client: can't find number in X display: %s\n", q); clean_up_exit(1); } if (vdpy == -1 && vnc_redirect != 2) { rfbLog("wait_for_client: can't find number in X display: %s\n", q); clean_up_exit(1); } if (vnc_redirect == 2) { if (vdpy < 0) { vdpy = -vdpy; } else if (vdpy < 200) { vdpy += 5900; } } else { vdpy += 5900; } if (created_disp) { usleep(1000*1000); } for (i=0; i < 20; i++) { sock = rfbConnectToTcpAddr(vnc_redirect_host, vdpy); if (sock >= 0) { break; } rfbLog("wait_for_client: ...\n"); usleep(500*1000); } if (sock < 0) { rfbLog("wait_for_client: could not connect to a VNC Server at %s:%d\n", vnc_redirect_host, vdpy); clean_up_exit(1); } if (inetd) { s_in = fileno(stdin); s_out = fileno(stdout); } else { s_in = s_out = vnc_redirect_sock; } if (vnc_redirect_cnt > 0) { write(vnc_redirect_sock, vnc_redirect_test, vnc_redirect_cnt); } rfbLog("wait_for_client: switching control to VNC Server at %s:%d\n", vnc_redirect_host, vdpy); raw_xfer(sock, s_in, s_out); } extern char find_display[]; extern char create_display[]; char *setup_cmd(char *str, int *vnc_redirect, char **vnc_redirect_host, int *vnc_redirect_port, int db) { char *cmd = NULL; if (no_external_cmds || !cmd_ok("WAIT")) { rfbLog("wait_for_client external cmds not allowed:" " %s\n", use_dpy); clean_up_exit(1); } cmd = str + strlen("cmd="); if (!strcmp(cmd, "FINDDISPLAY-print")) { fprintf(stdout, "%s", find_display); clean_up_exit(0); } if (!strcmp(cmd, "FINDDISPLAY-run")) { char tmp[] = "/tmp/fd.XXXXXX"; char com[100]; int fd = mkstemp(tmp); if (fd >= 0) { write(fd, find_display, strlen(find_display)); close(fd); set_env("FINDDISPLAY_run", "1"); sprintf(com, "/bin/sh %s -n; rm -f %s", tmp, tmp); system(com); } unlink(tmp); exit(0); } if (!strcmp(str, "FINDCREATEDISPLAY-print")) { fprintf(stdout, "%s", create_display); clean_up_exit(0); } if (db) fprintf(stderr, "cmd: %s\n", cmd); if (strstr(str, "FINDCREATEDISPLAY") || strstr(str, "FINDDISPLAY")) { if (strstr(str, "Xvnc.redirect") || strstr(str, "X.redirect")) { *vnc_redirect = 1; } } if (strstr(cmd, "FINDDISPLAY-vnc_redirect") == cmd) { int p; char h[256]; if (strlen(cmd) >= 256) { rfbLog("wait_for_client string too long: %s\n", str); clean_up_exit(1); } h[0] = '\0'; if (sscanf(cmd, "FINDDISPLAY-vnc_redirect=%d", &p) == 1) { ; } else if (sscanf(cmd, "FINDDISPLAY-vnc_redirect=%s %d", h, &p) == 2) { ; } else { rfbLog("wait_for_client bad string: %s\n", cmd); clean_up_exit(1); } *vnc_redirect_port = p; if (strcmp(h, "")) { *vnc_redirect_host = strdup(h); } *vnc_redirect = 2; rfbLog("wait_for_client: vnc_redirect: %s:%d\n", *vnc_redirect_host, *vnc_redirect_port); } return cmd; } static char *build_create_cmd(char *cmd, int *saw_xdmcp, char *usslpeer, char *tmp) { char *create_cmd = NULL; char *opts = strchr(cmd, '-'); char st[] = ""; char fdgeom[128], fdsess[128], fdopts[128], fdprog[128]; char fdxsrv[128], fdxdum[128], fdcups[128], fdesd[128]; char fdnas[128], fdsmb[128], fdtag[128]; char cdout[128]; if (opts) { opts++; if (strstr(opts, "xdmcp")) { *saw_xdmcp = 1; } } else { opts = st; } sprintf(fdgeom, "NONE"); fdsess[0] = '\0'; fdgeom[0] = '\0'; fdopts[0] = '\0'; fdprog[0] = '\0'; fdxsrv[0] = '\0'; fdxdum[0] = '\0'; fdcups[0] = '\0'; fdesd[0] = '\0'; fdnas[0] = '\0'; fdsmb[0] = '\0'; fdtag[0] = '\0'; cdout[0] = '\0'; if (unixpw && keep_unixpw_opts && keep_unixpw_opts[0] != '\0') { char *q, *p, *t = strdup(keep_unixpw_opts); if (strstr(t, "gnome")) { sprintf(fdsess, "gnome"); } else if (strstr(t, "kde")) { sprintf(fdsess, "kde"); } else if (strstr(t, "twm")) { sprintf(fdsess, "twm"); } else if (strstr(t, "fvwm")) { sprintf(fdsess, "fvwm"); } else if (strstr(t, "mwm")) { sprintf(fdsess, "mwm"); } else if (strstr(t, "cde")) { sprintf(fdsess, "cde"); } else if (strstr(t, "dtwm")) { sprintf(fdsess, "dtwm"); } else if (strstr(t, "xterm")) { sprintf(fdsess, "xterm"); } else if (strstr(t, "wmaker")) { sprintf(fdsess, "wmaker"); } else if (strstr(t, "xfce")) { sprintf(fdsess, "xfce"); } else if (strstr(t, "enlightenment")) { sprintf(fdsess, "enlightenment"); } else if (strstr(t, "Xsession")) { sprintf(fdsess, "Xsession"); } else if (strstr(t, "failsafe")) { sprintf(fdsess, "failsafe"); } q = strstr(t, "ge="); if (! q) q = strstr(t, "geom="); if (! q) q = strstr(t, "geometry="); if (q) { int ok = 1; q = strstr(q, "="); q++; p = strstr(q, ","); if (p) *p = '\0'; p = q; while (*p) { if (*p == 'x') { ; } else if (isdigit((int) *p)) { ; } else { ok = 0; break; } p++; } if (ok && strlen(q) < 32) { sprintf(fdgeom, q); if (!quiet) { rfbLog("set create display geom: %s\n", fdgeom); } } } q = strstr(t, "cups="); if (q) { int p; if (sscanf(q, "cups=%d", &p) == 1) { sprintf(fdcups, "%d", p); } } q = strstr(t, "esd="); if (q) { int p; if (sscanf(q, "esd=%d", &p) == 1) { sprintf(fdesd, "%d", p); } } free(t); } if (fdgeom[0] == '\0' && getenv("FD_GEOM")) { snprintf(fdgeom, 120, "%s", getenv("FD_GEOM")); } if (fdsess[0] == '\0' && getenv("FD_SESS")) { snprintf(fdsess, 120, "%s", getenv("FD_SESS")); } if (fdopts[0] == '\0' && getenv("FD_OPTS")) { snprintf(fdopts, 120, "%s", getenv("FD_OPTS")); } if (fdprog[0] == '\0' && getenv("FD_PROG")) { snprintf(fdprog, 120, "%s", getenv("FD_PROG")); } if (fdxsrv[0] == '\0' && getenv("FD_XSRV")) { snprintf(fdxsrv, 120, "%s", getenv("FD_XSRV")); } if (fdcups[0] == '\0' && getenv("FD_CUPS")) { snprintf(fdcups, 120, "%s", getenv("FD_CUPS")); } if (fdesd[0] == '\0' && getenv("FD_ESD")) { snprintf(fdesd, 120, "%s", getenv("FD_ESD")); } if (fdnas[0] == '\0' && getenv("FD_NAS")) { snprintf(fdnas, 120, "%s", getenv("FD_NAS")); } if (fdsmb[0] == '\0' && getenv("FD_SMB")) { snprintf(fdsmb, 120, "%s", getenv("FD_SMB")); } if (fdtag[0] == '\0' && getenv("FD_TAG")) { snprintf(fdtag, 120, "%s", getenv("FD_TAG")); } if (fdxdum[0] == '\0' && getenv("FD_XDUMMY_NOROOT")) { snprintf(fdxdum, 120, "%s", getenv("FD_XDUMMY_NOROOT")); } if (getenv("CREATE_DISPLAY_OUTPUT")) { snprintf(cdout, 120, "CREATE_DISPLAY_OUTPUT='%s'", getenv("CREATE_DISPLAY_OUTPUT")); } set_env("FD_GEOM", fdgeom); set_env("FD_OPTS", fdopts); set_env("FD_PROG", fdprog); set_env("FD_XSRV", fdxsrv); set_env("FD_CUPS", fdcups); set_env("FD_ESD", fdesd); set_env("FD_NAS", fdnas); set_env("FD_SMB", fdsmb); set_env("FD_TAG", fdtag); set_env("FD_XDUMMY_NOROOT", fdxdum); set_env("FD_SESS", fdsess); if (usslpeer || (unixpw && keep_unixpw_user)) { char *uu = usslpeer; if (!uu) { uu = keep_unixpw_user; } create_cmd = (char *) malloc(strlen(tmp)+1 + strlen("env USER='' ") + strlen("FD_GEOM='' ") + strlen("FD_OPTS='' ") + strlen("FD_PROG='' ") + strlen("FD_XSRV='' ") + strlen("FD_CUPS='' ") + strlen("FD_ESD='' ") + strlen("FD_NAS='' ") + strlen("FD_SMB='' ") + strlen("FD_TAG='' ") + strlen("FD_XDUMMY_NOROOT='' ") + strlen("FD_SESS='' /bin/sh ") + strlen(uu) + 1 + strlen(fdgeom) + 1 + strlen(fdopts) + 1 + strlen(fdprog) + 1 + strlen(fdxsrv) + 1 + strlen(fdcups) + 1 + strlen(fdesd) + 1 + strlen(fdnas) + 1 + strlen(fdsmb) + 1 + strlen(fdtag) + 1 + strlen(fdxdum) + 1 + strlen(fdsess) + 1 + strlen(cdout) + 1 + strlen(opts) + 1); sprintf(create_cmd, "env USER='%s' FD_GEOM='%s' FD_SESS='%s' " "FD_OPTS='%s' FD_PROG='%s' FD_XSRV='%s' FD_CUPS='%s' " "FD_ESD='%s' FD_NAS='%s' FD_SMB='%s' FD_TAG='%s' " "FD_XDUMMY_NOROOT='%s' %s /bin/sh %s %s", uu, fdgeom, fdsess, fdopts, fdprog, fdxsrv, fdcups, fdesd, fdnas, fdsmb, fdtag, fdxdum, cdout, tmp, opts); } else { create_cmd = (char *) malloc(strlen(tmp) + strlen("/bin/sh ") + 1 + strlen(opts) + 1); sprintf(create_cmd, "/bin/sh %s %s", tmp, opts); } return create_cmd; } static char *certret_extract() { char *q, *p, *str = strdup(certret_str); char *upeer = NULL; int ok = 0; q = strstr(str, "Subject: "); if (! q) return NULL; p = strstr(q, "\n"); if (p) *p = '\0'; q = strstr(q, "CN="); if (! q) return NULL; if (! getenv("X11VNC_SSLPEER_CN")) { p = q; q = strstr(q, "/emailAddress="); if (! q) q = strstr(p, "/Email="); if (! q) return NULL; } q = strstr(q, "="); if (! q) return NULL; q++; p = strstr(q, " "); if (p) *p = '\0'; p = strstr(q, "@"); if (p) *p = '\0'; p = strstr(q, "/"); if (p) *p = '\0'; upeer = strdup(q); if (strcmp(upeer, "")) { p = upeer; while (*p != '\0') { char c = *p; if (!isalnum((int) c)) { *p = '\0'; break; } p++; } if (strcmp(upeer, "")) { ok = 1; } } if (! ok) { upeer = NULL; } return upeer; } static void check_nodisplay(char **nd) { if (unixpw && keep_unixpw_opts && keep_unixpw_opts[0] != '\0') { char *q, *t = keep_unixpw_opts; q = strstr(t, "nd="); if (! q) q = strstr(t, "nodisplay="); if (q) { char *t2; q = strchr(q, '=') + 1; t = strdup(q); q = t; t2 = strchr(t, ','); if (t2) *t2 = '\0'; while (*t != '\0') { if (*t == '+') { *t = ','; } t++; } if (!strchr(q, '\'')) { if (! quiet) rfbLog("set X11VNC_SKIP_DISPLAY: %s\n", q); *nd = q; } } } } static char *get_usslpeer() { char *u = NULL, *upeer = NULL; if (certret_str) { upeer = certret_extract(); } if (!upeer) { return NULL; } rfbLog("sslpeer unix username extracted from x509 cert: %s\n", upeer); u = (char *) malloc(strlen(upeer+2)); u[0] = '\0'; if (!strcmp(users_list, "sslpeer=")) { sprintf(u, "+%s", upeer); } else { char *p, *str = strdup(users_list); p = strtok(str + strlen("sslpeer="), ","); while (p) { if (!strcmp(p, upeer)) { sprintf(u, "+%s", upeer); break; } p = strtok(NULL, ","); } free(str); } if (u[0] == '\0') { rfbLog("sslpeer cannot determine user: %s\n", upeer); free(u); return NULL; } free(u); return upeer; } static int do_run_cmd(char *cmd, char *create_cmd, char *users_list_save, int created_disp, int db) { char tmp[] = "/tmp/x11vnc-find_display.XXXXXX"; char line1[1024], line2[16384]; char *q, *usslpeer = NULL; int n, nodisp = 0, saw_xdmcp = 0; int tmp_fd = -1; memset(line1, 0, 1024); memset(line2, 0, 16384); if (users_list && strstr(users_list, "sslpeer=") == users_list) { usslpeer = get_usslpeer(); if (! usslpeer) { return 0; } } /* only sets environment variables: */ run_user_command("", latest_client, "env", NULL, 0, NULL); if (program_name) { set_env("X11VNC_PROG", program_name); } else { set_env("X11VNC_PROG", "x11vnc"); } if (!strcmp(cmd, "FINDDISPLAY") || strstr(cmd, "FINDCREATEDISPLAY") == cmd) { char *nd = ""; char fdout[128]; tmp_fd = mkstemp(tmp); if (tmp_fd < 0) { rfbLog("wait_for_client: open failed: %s\n", tmp); rfbLogPerror("mkstemp"); clean_up_exit(1); } chmod(tmp, 0644); if (getenv("X11VNC_FINDDISPLAY_ALWAYS_FAILS")) { char *s = "#!/bin/sh\necho _FAIL_\nexit 1\n"; write(tmp_fd, s, strlen(s)); } else { write(tmp_fd, find_display, strlen(find_display)); } close(tmp_fd); nodisp = 1; if (strstr(cmd, "FINDCREATEDISPLAY") == cmd) { create_cmd = build_create_cmd(cmd, &saw_xdmcp, usslpeer, tmp); if (db) fprintf(stderr, "create_cmd: %s\n", create_cmd); } if (getenv("X11VNC_SKIP_DISPLAY")) { nd = strdup(getenv("X11VNC_SKIP_DISPLAY")); } check_nodisplay(&nd); fdout[0] = '\0'; if (getenv("FIND_DISPLAY_OUTPUT")) { snprintf(fdout, 120, " FIND_DISPLAY_OUTPUT='%s' ", getenv("FIND_DISPLAY_OUTPUT")); } cmd = (char *) malloc(strlen("env X11VNC_SKIP_DISPLAY='' ") + strlen(nd) + strlen(tmp) + strlen("/bin/sh ") + strlen(fdout) + 1); sprintf(cmd, "env X11VNC_SKIP_DISPLAY='%s' %s /bin/sh %s", nd, fdout, tmp); } rfbLog("wait_for_client: running: %s\n", cmd); if (unixpw) { int res = 0, k, j, i; char line[18000]; memset(line, 0, 18000); if (keep_unixpw_user && keep_unixpw_pass) { n = 18000; res = su_verify(keep_unixpw_user, keep_unixpw_pass, cmd, line, &n, nodisp); } if (db) {fprintf(stderr, "line: "); write(2, line, n); write(2, "\n", 1); fprintf(stderr, "res=%d n=%d\n", res, n);} if (! res) { rfbLog("wait_for_client: find display cmd failed\n"); } if (! res && create_cmd) { FILE *mt = fopen(tmp, "w"); if (! mt) { rfbLog("wait_for_client: open failed: %s\n", tmp); rfbLogPerror("fopen"); clean_up_exit(1); } fprintf(mt, "%s", create_display); fclose(mt); findcreatedisplay = 1; if (getuid() != 0) { /* if not root, run as the other user... */ n = 18000; close_exec_fds(); res = su_verify(keep_unixpw_user, keep_unixpw_pass, create_cmd, line, &n, nodisp); if (db) fprintf(stderr, "c-res=%d n=%d line: '%s'\n", res, n, line); } else { FILE *p; close_exec_fds(); rfbLog("wait_for_client: running: %s\n", create_cmd); p = popen(create_cmd, "r"); if (! p) { rfbLog("wait_for_client: popen failed: %s\n", create_cmd); res = 0; } else if (fgets(line1, 1024, p) == NULL) { rfbLog("wait_for_client: read failed: %s\n", create_cmd); res = 0; } else { n = fread(line2, 1, 16384, p); if (pclose(p) != 0) { res = 0; } else { strncpy(line, line1, 100); memcpy(line + strlen(line1), line2, n); if (db) fprintf(stderr, "line1: '%s'\n", line1); n += strlen(line1); created_disp = 1; res = 1; } } } if (res && saw_xdmcp) { xdmcp_insert = strdup(keep_unixpw_user); } } if (tmp_fd >= 0) { unlink(tmp); } if (! res) { rfbLog("wait_for_client: cmd failed: %s\n", cmd); unixpw_msg("No DISPLAY found.", 3); clean_up_exit(1); } /* * we need to hunt for DISPLAY= since there may be * a login banner or something at the beginning. */ q = strstr(line, "DISPLAY="); if (! q) { q = line; } n -= (q - line); for (k = 0; k < 1024; k++) { line1[k] = q[k]; if (q[k] == '\n') { k++; break; } } n -= k; i = 0; for (j = 0; j < 16384; j++) { if (j < 16384 - 1) { /* xauth data, assume pty added CR */ if (q[k+j] == '\r' && q[k+j+1] == '\n') { continue; } } line2[i] = q[k+j]; i++; } if (db) write(2, line, 100); if (db) fprintf(stderr, "\n"); } else { FILE *p; int rc; close_exec_fds(); if (usslpeer) { char *c; if (getuid() == 0) { c = (char *) malloc(strlen("su - '' -c \"") + strlen(usslpeer) + strlen(cmd) + 1 + 1); sprintf(c, "su - '%s' -c \"%s\"", usslpeer, cmd); } else { c = strdup(cmd); } p = popen(c, "r"); free(c); } else { p = popen(cmd, "r"); } if (! p) { rfbLog("wait_for_client: cmd failed: %s\n", cmd); rfbLogPerror("popen"); if (tmp_fd >= 0) { unlink(tmp); } clean_up_exit(1); } if (fgets(line1, 1024, p) == NULL) { rfbLog("wait_for_client: read failed: %s\n", cmd); rfbLogPerror("fgets"); if (tmp_fd >= 0) { unlink(tmp); } clean_up_exit(1); } n = fread(line2, 1, 16384, p); rc = pclose(p); if (rc != 0) { rfbLog("wait_for_client: find display cmd failed\n"); } if (create_cmd && rc != 0) { FILE *mt = fopen(tmp, "w"); if (! mt) { rfbLog("wait_for_client: open failed: %s\n", tmp); rfbLogPerror("fopen"); if (tmp_fd >= 0) { unlink(tmp); } clean_up_exit(1); } fprintf(mt, "%s", create_display); fclose(mt); findcreatedisplay = 1; rfbLog("wait_for_client: FINDCREATEDISPLAY cmd: %s\n", create_cmd); p = popen(create_cmd, "r"); if (! p) { rfbLog("wait_for_client: cmd failed: %s\n", create_cmd); rfbLogPerror("popen"); if (tmp_fd >= 0) { unlink(tmp); } clean_up_exit(1); } if (fgets(line1, 1024, p) == NULL) { rfbLog("wait_for_client: read failed: %s\n", create_cmd); rfbLogPerror("fgets"); if (tmp_fd >= 0) { unlink(tmp); } clean_up_exit(1); } n = fread(line2, 1, 16384, p); } if (tmp_fd >= 0) { unlink(tmp); } } if (db) fprintf(stderr, "line1=%s\n", line1); if (strstr(line1, "DISPLAY=") != line1) { rfbLog("wait_for_client: bad reply '%s'\n", line1); if (unixpw) { unixpw_msg("No DISPLAY found.", 3); } clean_up_exit(1); } if (strstr(line1, ",VT=")) { int vt; char *t = strstr(line1, ",VT="); vt = atoi(t + strlen(",VT=")); *t = '\0'; if (7 <= vt && vt <= 15) { do_chvt(vt); } } else if (strstr(line1, ",XPID=")) { int i, pvt, vt = -1; char *t = strstr(line1, ",XPID="); pvt = atoi(t + strlen(",XPID=")); *t = '\0'; if (pvt > 0) { for (i=3; i <= 10; i++) { int k; char proc[100]; char buf[100]; sprintf(proc, "/proc/%d/fd/%d", pvt, i); if (db) fprintf(stderr, "%d -- %s\n", i, proc); for (k=0; k < 100; k++) { buf[k] = '\0'; } if (readlink(proc, buf, 100) != -1) { buf[100-1] = '\0'; if (db) fprintf(stderr, "%d -- %s -- %s\n", i, proc, buf); if (strstr(buf, "/dev/tty") == buf) { vt = atoi(buf + strlen("/dev/tty")); if (vt > 0) { break; } } } } } if (7 <= vt && vt <= 12) { do_chvt(vt); } } use_dpy = strdup(line1 + strlen("DISPLAY=")); q = use_dpy; while (*q != '\0') { if (*q == '\n' || *q == '\r') *q = '\0'; q++; } if (line2[0] != '\0') { if (strstr(line2, "XAUTHORITY=") == line2) { q = line2; while (*q != '\0') { if (*q == '\n' || *q == '\r') *q = '\0'; q++; } if (auth_file) { free(auth_file); } auth_file = strdup(line2 + strlen("XAUTHORITY=")); } else { xauth_raw_data = (char *)malloc(n); xauth_raw_len = n; memcpy(xauth_raw_data, line2, n); if (db) {fprintf(stderr, "xauth_raw_len: %d\n", n); write(2, xauth_raw_data, n); fprintf(stderr, "\n");} } } if (usslpeer) { char *u = (char *) malloc(strlen(usslpeer+2)); sprintf(u, "+%s", usslpeer); if (switch_user(u, 0)) { rfbLog("sslpeer switched to user: %s\n", usslpeer); } else { rfbLog("sslpeer failed to switch to user: %s\n", usslpeer); } free(u); } else if (users_list_save && keep_unixpw_user) { char *user = keep_unixpw_user; char *u = (char *)malloc(strlen(user)+1); users_list = users_list_save; u[0] = '\0'; if (!strcmp(users_list, "unixpw=")) { sprintf(u, "+%s", user); } else { char *p, *str = strdup(users_list); p = strtok(str + strlen("unixpw="), ","); while (p) { if (!strcmp(p, user)) { sprintf(u, "+%s", user); break; } p = strtok(NULL, ","); } free(str); } if (u[0] == '\0') { rfbLog("unixpw_accept skipping switch to user: %s\n", user); } else if (switch_user(u, 0)) { rfbLog("unixpw_accept switched to user: %s\n", user); } else { rfbLog("unixpw_accept failed to switch to user: %s\n", user); } free(u); } if (unixpw) { char str[32]; if (keep_unixpw_user && keep_unixpw_pass) { strzero(keep_unixpw_user); strzero(keep_unixpw_pass); keep_unixpw = 0; } if (created_disp) { snprintf(str, 30, "Created DISPLAY %s", use_dpy); } else { snprintf(str, 30, "Using DISPLAY %s", use_dpy); } unixpw_msg(str, 2); } return 1; } void ssh_remote_tunnel(char *, int); static XImage ximage_struct; void progress_client(void) { int i, j = 0, progressed = 0, db = 0; double start = dnow(); if (getenv("PROGRESS_CLIENT_DBG")) { rfbLog("progress_client: begin\n"); db = 1; } for (i = 0; i < 15; i++) { if (latest_client) { for (j = 0; j < 10; j++) { if (latest_client->state != RFB_PROTOCOL_VERSION) { progressed = 1; break; } if (db) rfbLog("progress_client: calling-1 rfbCFD(1) %.6f\n", dnow()-start); rfbCFD(1); } } if (progressed) { break; } if (db) rfbLog("progress_client: calling-2 rfbCFD(1) %.6f\n", dnow()-start); rfbCFD(1); } if (!quiet) { rfbLog("client progressed=%d in %d/%d %.6f s\n", progressed, i, j, dnow() - start); } } int wait_for_client(int *argc, char** argv, int http) { /* ugh, here we go... */ XImage* fb_image; int w = 640, h = 480, b = 32; int w0 = -1, h0 = -1, i, chg_raw_fb = 0; char *str, *q, *cmd = NULL; int db = 0, dt = 0; char *create_cmd = NULL; char *users_list_save = NULL; int created_disp = 0, ncache_save; int did_client_connect = 0; char *vnc_redirect_host = "localhost"; int vnc_redirect_port = -1, vnc_redirect_cnt = 0; char vnc_redirect_test[10]; if (getenv("WAIT_FOR_CLIENT_DB")) { db = 1; } vnc_redirect = 0; if (! use_dpy || strstr(use_dpy, "WAIT:") != use_dpy) { return 0; } for (i=0; i < *argc; i++) { if (!strcmp(argv[i], "-desktop")) { dt = 1; } if (db) fprintf(stderr, "args %d %s\n", i, argv[i]); } if (!quiet && !strstr(use_dpy, "FINDDISPLAY-run")) { rfbLog("wait_for_client: %s\n", use_dpy); } str = strdup(use_dpy); str += strlen("WAIT"); xdmcp_insert = NULL; /* get any leading geometry: */ q = strchr(str+1, ':'); if (q) { *q = '\0'; if (sscanf(str+1, "%dx%d", &w0, &h0) == 2) { w = w0; h = h0; rfbLog("wait_for_client set: w=%d h=%d\n", w, h); } else { w0 = -1; h0 = -1; } *q = ':'; str = q; } if ((w0 == -1 || h0 == -1) && pad_geometry != NULL) { int b0, del = 0; char *s = pad_geometry; if (strstr(s, "once:") == s) { del = 1; s += strlen("once:"); } if (sscanf(s, "%dx%dx%d", &w0, &h0, &b0) == 3) { w = nabs(w0); h = nabs(h0); b = nabs(b0); } else if (sscanf(s, "%dx%d", &w0, &h0) == 2) { w = nabs(w0); h = nabs(h0); } if (del) { pad_geometry = NULL; } } /* str currently begins with a ':' */ if (strstr(str, ":cmd=") == str) { /* cmd=/path/to/mycommand */ str++; } else if (strpbrk(str, "0123456789") == str+1) { /* :0.0 */ ; } else { /* hostname:0.0 */ str++; } if (db) fprintf(stderr, "str: %s\n", str); if (strstr(str, "cmd=") == str) { cmd = setup_cmd(str, &vnc_redirect, &vnc_redirect_host, &vnc_redirect_port, db); } fb_image = &ximage_struct; setup_fake_fb(fb_image, w, h, b); if (! dt) { char *s; argv[*argc] = strdup("-desktop"); *argc = (*argc) + 1; if (cmd) { char *q; s = choose_title(":0"); q = strstr(s, ":0"); if (q) { *q = '\0'; } } else { s = choose_title(str); } rfb_desktop_name = strdup(s); argv[*argc] = s; *argc = (*argc) + 1; } ncache_save = ncache; ncache = 0; initialize_allowed_input(); if (! multiple_cursors_mode) { multiple_cursors_mode = strdup("default"); } initialize_cursors_mode(); initialize_screen(argc, argv, fb_image); initialize_signals(); if (ssh_str != NULL) { ssh_remote_tunnel(ssh_str, screen->port); } if (! raw_fb) { chg_raw_fb = 1; /* kludge to get RAWFB_RET with dpy == NULL guards */ raw_fb = (char *) 0x1; } if (cmd && !strcmp(cmd, "HTTPONCE")) { handle_one_http_request(); clean_up_exit(0); } if (http && check_httpdir()) { http_connections(1); } if (cmd && unixpw) { keep_unixpw = 1; } setup_service(); check_waitbg(); if (vnc_redirect) { vnc_redirect_loop(vnc_redirect_test, &vnc_redirect_cnt); } else { if (inetd && use_openssl) { accept_openssl(OPENSSL_INETD, -1); } setup_client_connect(&did_client_connect); loop_for_connect(did_client_connect); if (unixpw) { if (cmd && strstr(cmd, "FINDCREATEDISPLAY") == cmd) { if (users_list && strstr(users_list, "unixpw=") == users_list) { users_list_save = users_list; users_list = NULL; } } do_unixpw_loop(); } else if (cmd && !use_threads) { /* try to get RFB proto done now. */ progress_client(); } } if (vnc_redirect == 2) { ; } else if (cmd) { if (!do_run_cmd(cmd, create_cmd, users_list_save, created_disp, db)) { return 0; } } else { use_dpy = strdup(str); } if (chg_raw_fb) { raw_fb = NULL; } ncache = ncache_save; if (unixpw && keep_unixpw_opts && keep_unixpw_opts[0] != '\0') { user_supplied_opts(keep_unixpw_opts); } if (create_cmd) { free(create_cmd); } if (vnc_redirect) { do_vnc_redirect(created_disp, vnc_redirect_host, vnc_redirect_port, vnc_redirect_cnt, vnc_redirect_test); clean_up_exit(0); } return 1; }