/* -- 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 "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); 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 **) malloc((n+1)*sizeof(char *)); p = strtok(user_str, ","); i = 0; while (p) { list[i++] = 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; 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); } p = strtok(logins, ","); while (p) { char *user, *name, *home, dpystr[10]; char *q, *t; int ok = 1, dn; 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) { p = strtok(NULL, ","); 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; } p = strtok(NULL, ","); } free(logins); 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; 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); p = strtok(logins, ","); while (p) { char *user, *q, *t; int dpy2, ok = 1; 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); p = strtok(NULL, ","); continue; } if (switch_user(user, fb_mode)) { rfbLog("switched to guessed user: %s\n", user); free(t); ret = 1; break; } p = strtok(NULL, ","); } 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++; } 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 { #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", "speeds", "sp", "readtimeout", "rd", "rotate", "ro", "geometry", "geom", "ge", "noncache", "nc", "nodisplay", "nd", 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 (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, "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); } extern char find_display[]; extern char create_display[]; static XImage ximage_struct; int wait_for_client(int *argc, char** argv, int http) { XImage* fb_image; int w = 640, h = 480, b = 32; int w0, h0, i, chg_raw_fb = 0; char *str, *q, *cmd = NULL; int db = 0; char tmp[] = "/tmp/x11vnc-find_display.XXXXXX"; int tmp_fd = -1, dt = 0; char *create_cmd = NULL; char *users_list_save = NULL; int created_disp = 0; int ncache_save; int did_client_connect = 0; int loop = 0; if (! use_dpy || strstr(use_dpy, "WAIT:") != use_dpy) { return 0; } if (getenv("WAIT_FOR_CLIENT_DB")) { db = 1; } for (i=0; i < *argc; i++) { if (!strcmp(argv[i], "-desktop")) { dt = 1; } if (db) fprintf(stderr, "args %d %s\n", i, argv[i]); } 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); } *q = ':'; str = q; } /* 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) { 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(str, "FINDDISPLAY-print")) { fprintf(stdout, "%s", find_display); clean_up_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 (fake_fb) { free(fake_fb); } fake_fb = (char *) calloc(w*h*b/8, 1); fb_image = &ximage_struct; 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; fb_image->depth = 24; fb_image->red_mask = 0xff0000; fb_image->green_mask = 0x00ff00; fb_image->blue_mask = 0x0000ff; depth = fb_image->depth; dpy_x = wdpy_x = w; dpy_y = wdpy_y = h; off_x = 0; off_y = 0; 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 (! 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; } 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); } 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 } if (inetd && use_openssl) { accept_openssl(OPENSSL_INETD, -1); } 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; } } while (1) { loop++; 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(); /* * 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; } 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; } if (cmd && strstr(cmd, "FINDCREATEDISPLAY") == cmd) { if (users_list && strstr(users_list, "unixpw=") == users_list) { users_list_save = users_list; users_list = NULL; } } 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; } } if (0) db = 1; if (cmd) { char line1[1024]; char line2[16384]; char *q; int n; int nodisp = 0; int saw_xdmcp = 0; char *usslpeer = NULL; memset(line1, 0, 1024); memset(line2, 0, 16384); if (users_list && strstr(users_list, "sslpeer=") == users_list) { int ok = 0; char *u = NULL, *upeer = NULL; if (certret_str) { char *q, *p, *str = strdup(certret_str); q = strstr(str, "Subject: "); if (! q) return 0; p = strstr(q, "\n"); if (p) *p = '\0'; q = strstr(q, "CN="); if (! q) return 0; if (! getenv("X11VNC_SSLPEER_CN")) { p = q; q = strstr(q, "/emailAddress="); if (! q) q = strstr(p, "/Email="); if (! q) return 0; } q = strstr(q, "="); if (! q) return 0; 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) { return 0; } 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 0; } free(u); usslpeer = upeer; } /* only sets environment variables: */ run_user_command("", latest_client, "env", NULL, 0, NULL); if (!strcmp(cmd, "FINDDISPLAY") || strstr(cmd, "FINDCREATEDISPLAY") == cmd) { char *nd = ""; 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) { char *opts = strchr(cmd, '-'); char st[] = ""; char geom[32], xsess[32]; if (opts) { opts++; if (strstr(opts, "xdmcp")) { saw_xdmcp = 1; } } else { opts = st; } sprintf(geom, "NONE"); xsess[0] = '\0'; geom[0] = '\0'; #if 0 if (!keep_unixpw_opts) { fprintf(stderr, "no keep_unixpw_opts\n"); } else { fprintf(stderr, "keep_unixpw_opts: %s\n", keep_unixpw_opts); } #endif if (unixpw && keep_unixpw_opts && keep_unixpw_opts[0] != '\0') { char *q, *p, *t = strdup(keep_unixpw_opts); if (strstr(t, "gnome")) { sprintf(xsess, "gnome"); } else if (strstr(t, "kde")) { sprintf(xsess, "kde"); } else if (strstr(t, "twm")) { sprintf(xsess, "twm"); } else if (strstr(t, "fvwm")) { sprintf(xsess, "fvwm"); } else if (strstr(t, "mwm")) { sprintf(xsess, "mwm"); } else if (strstr(t, "failsafe")) { sprintf(xsess, "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(geom, q); if (!quiet) { rfbLog("set create display geom: %s\n", geom); } } } free(t); } if (geom[0] == '\0' && getenv("FD_GEOM")) { snprintf(geom, 30, "%s", getenv("FD_GEOM")); } if (xsess[0] == '\0' && getenv("FD_SESS")) { snprintf(xsess, 30, "%s", getenv("FD_SESS")); } set_env("FD_GEOM", geom); set_env("FD_SESS", xsess); 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_SESS='' /bin/sh ") + strlen(uu) + 1 + strlen(geom) + 1 + strlen(xsess) + 1 + strlen(opts) + 1); sprintf(create_cmd, "env USER='%s' FD_GEOM='%s' FD_SESS='%s' /bin/sh %s %s", uu, geom, xsess, 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); } if (db) fprintf(stderr, "create_cmd: %s\n", create_cmd); } if (getenv("X11VNC_SKIP_DISPLAY")) { nd = strdup(getenv("X11VNC_SKIP_DISPLAY")); } 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; } } } cmd = (char *) malloc(strlen("env X11VNC_SKIP_DISPLAY='' ") + strlen(nd) + strlen(tmp) + strlen("/bin/sh ") + 1); sprintf(cmd, "env X11VNC_SKIP_DISPLAY='%s' /bin/sh %s", nd, 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 <= 128) { char chvt[100]; sprintf(chvt, "chvt %d >/dev/null 2>/dev/null &", vt); rfbLog("running: %s\n", chvt); system(chvt); sleep(2); } } 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) { char chvt[100]; sprintf(chvt, "chvt %d >/dev/null 2>/dev/null &", vt); rfbLog("running: %s\n", chvt); system(chvt); sleep(2); } } 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); } } 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); } return 1; }