/* * Copyright (c) 1983 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * (BSD License, from tdelibs/doc/common/bsd-license.html) */ #include "includ.h" #include #ifdef HAVE_SYS_STAT_H #include #endif #include #include #include #include #define USE_UT_HOST #ifndef UT_HOSTSIZE #define UT_HOSTSIZE 12 /*whatever*/ #undef USE_UT_HOST #endif #include #include #include #include #include #ifdef ALL_PROCESSES_AND_PROC_FIND_USER #include #include #include #endif #include "proto.h" #include "defs.h" #ifdef PROC_FIND_USER #define DISPLAYS_LIST_MAX 200 #define DISPLAY_MAX 50 /* get DISPLAY variable of a process */ char *get_display(pid_t pid) { static char buf[1024]; sprintf(buf, "/proc/%d/environ", pid); std::ifstream ifstr(buf); if (ifstr.fail()) return 0; /* ktalk_debug("reading %s...", fname); */ char * dpy = 0; while (!ifstr.eof()) { ifstr.getline(buf, sizeof buf - 1, '\0'); if (ifstr.fail()) { syslog(LOG_ERR, "getline: %s", strerror(errno)); return 0; /* read error */ } if (!strncmp("DISPLAY=", buf, 8)) { int l = strlen(buf + 8); if( l >= DISPLAY_MAX ) return 0; if (l >= 2 && l < (int) sizeof buf - 1) { dpy = new char[ l+2 ]; strcpy(dpy," "); strcat(dpy, buf + 8); /* returns " "+dpy */ /* ktalk_debug("- %s",dpy); */ } break; } } return dpy; } /* As utmp isn't reliable (neither xdm nor tdm logs into it ! :( ), we have to look at processes directly. /proc helps a lot, under linux. How to do it under other unixes ? */ #ifdef ALL_PROCESSES_AND_PROC_FIND_USER /* awful global variable, but how to pass it to select_process() otherwise ? scandir() doesn't allow this, of course... */ unsigned int user_uid; /* selection function used by scandir */ int select_process( #ifdef SCANDIR_NEEDS_CONST const struct dirent *direntry #else struct dirent *direntry #endif ) { /* returns 1 if username owns */ struct stat statbuf; /* make absolute path (would sprintf be better ?)*/ char abspath[20]="/proc/"; if( strlen( direntry->d_name ) > 12 ) return 0; strcat(abspath,direntry->d_name); if (isdigit(direntry->d_name[0])) { /* starts with [0-9]*/ if (!stat(abspath, &statbuf)) { /* if exists */ if (S_ISDIR(statbuf.st_mode)) { /* and is a directory and */ if (statbuf.st_uid == user_uid) /* is owned by user_uid*/ { /* We have to force errno=0, because otherwise, scandir will stop ! */ /* the problem is that glibc sets errno in getpwnam and syslog (glibc-2.0.5c/6, with libstdc++ 2.7.2) */ errno=0; return 1; } /* else ktalk_debug("st_uid=%d", statbuf.st_uid); */ } /* else ktalk_debug("st_mode=%d", statbuf.st_mode); */ } else ktalk_debug("stat error : %s", strerror(errno)); } errno=0; return 0; } /* scan /proc for any process owned by 'name'. If DISPLAY is set, set 'disp'. Called only if no X utmp entry found. */ /* * Major memory leak, never frees the memory allocated by scandir * (why not use readdir() in the first place? * Major buffer overflow: If I set my DISPLAY variable to a * 1024 bytes string, it'll overflow displays_list. */ int find_X_process(char *name, char *disp) { char displays_list[DISPLAYS_LIST_MAX] = " "; /* yes, one space */ char * dispwithblanks; struct dirent **namelist; struct passwd * pw = getpwnam(name); ktalk_debug("find_X_process"); /* find uid */ if ((pw) && ((user_uid=pw->pw_uid)>10)) { /* uid<10 : no X detection because any suid program will be taken as owned by root, not by its real owner */ /* scan /proc */ int n = scandir("/proc", &namelist, select_process, 0 /*no sort*/); if (n < 0) ktalk_debug("scandir: %s", strerror(errno)); else while(n--) { /* find DISPLAY */ dispwithblanks = get_display(atoi(namelist[n]->d_name)); if (dispwithblanks) { /* This way, if :0.0 is in the list, :0 is not inserted */ /* XXX Yes, but if I have two displays, one foo:0.0 * and one bar:0.0, then bar:0 is not caught by the * check below. But this is just peanuts. */ if (!strstr(displays_list,dispwithblanks)) { /* not already in the list? */ char * pointlocation=strstr(dispwithblanks,"."); if (pointlocation) *pointlocation='\0'; if (!strstr(displays_list,dispwithblanks) && strlen(dispwithblanks)+strlen(displays_list)1) { strcpy(disp,displays_list+1); /* removes the leading white space but leave the final one, needed by announce.c */ return 1; } } /* if pw */ return 0; } #endif /* ALL_PROCESSES_AND_PROC_FIND_USER */ #ifdef UTMP_AND_PROC_FIND_USER /* * Search utmp for the local user * * Priorities: * login from xdm * login from pseudo terminal with $DISPLAY set * login from pseudo terminal * other login */ #define PRIO_LOGIN 1 /* user is logged in */ #define PRIO_PTY 2 /* user is logged in on a pseudo terminal */ #define PRIO_DISPLAY 3 /* user is logged in on a pseudo terminal and has $DISPLAY set. */ #define PRIO_XDM 4 /* user is logged in from xdm */ #define SCMPN(a, b) strncmp(a, b, sizeof (a)) int find_user(char *name, char *tty, char *disp) { struct utmp *ubuf; int prio = 0, status = NOT_HERE; struct stat statb; char ftty[20+UT_LINESIZE]; char *ntty, *dpy; char ttyFound[UT_LINESIZE] = ""; char dispFound[DISPLAYS_LIST_MAX] = ""; strcpy(ftty, _PATH_DEV); ntty = ftty + strlen(ftty); setutent(); while ((ubuf = getutent())) { if ((ubuf->ut_type == USER_PROCESS) && (!SCMPN(ubuf->ut_name, name))) { if (*tty == '\0') { /* no particular tty was requested */ if (Options.XAnnounce && ubuf->ut_line[0] == ':') { /* this is a XDM login (really?). GREAT! */ syslog(LOG_DEBUG, "XDM login: %s at %s", name, ubuf->ut_line); status = SUCCESS; if (prio < PRIO_XDM) { strcpy(dispFound, ubuf->ut_line); strcat(dispFound, " "); prio = PRIO_XDM; } continue; } strcpy(ntty, ubuf->ut_line); if (stat(ftty, &statb) != 0 || (!(statb.st_mode & 020))) { ktalk_debug("Permission denied on %s", ntty); continue; /* not a char dev */ } /* device exists and is a character device */ status = SUCCESS; if (Options.debug_mode) syslog(LOG_DEBUG, "Found %s at %s", name, ubuf->ut_line); if (prio < PRIO_LOGIN) { prio = PRIO_LOGIN; strcpy(ttyFound, ubuf->ut_line); *dispFound = '\0'; } /* the following code is Linux specific... * is there a portable way to * 1) determine if a device is a pseudo terminal and * 2) get environment variables of an arbitrary process? */ if (strncmp("tty", ubuf->ut_line, 3) != 0 || !strchr("pqrstuvwxyzabcde", ubuf->ut_line[3])) continue; /* not a pty */ /* device is a pseudo terminal (ex : a xterm) */ if (Options.debug_mode) syslog(LOG_DEBUG, "PTY %s, ut_host=%s", ubuf->ut_line, ubuf->ut_host); if (prio < PRIO_PTY) { prio = PRIO_PTY; strcpy(ttyFound, ubuf->ut_line); strcpy(dispFound, ubuf->ut_host); strcat(dispFound, " "); } dpy = get_display(ubuf->ut_pid); if (!dpy) continue; /* DISPLAY not set or empty */ /* $DISPLAY is set. */ if (Options.debug_mode) syslog(LOG_DEBUG, "Found display %s on %s", dpy, ubuf->ut_line); if (prio < PRIO_DISPLAY) { prio = PRIO_DISPLAY; strcpy(ttyFound, ubuf->ut_line); strcpy(dispFound, dpy+1); /*no space*/ strcat(dispFound, " "); } delete dpy; continue; } if (!strcmp(ubuf->ut_line, tty)) { strcpy(ttyFound, ubuf->ut_line); status = SUCCESS; break; } } } endutent(); ktalk_debug("End of Utmp reading"); #if defined(HAVE_KDE) && defined(ALL_PROCESSES_AND_PROC_FIND_USER) if (Options.XAnnounce && prio < PRIO_DISPLAY) if (find_X_process(name, dispFound)) { ktalk_debug(dispFound); status=SUCCESS; } #endif if (status == SUCCESS) { (void) strcpy(tty, ttyFound); (void) strcpy(disp, dispFound); if (Options.debug_mode) syslog(LOG_DEBUG, "Returning tty '%s', display '%s'", ttyFound, dispFound); } else ktalk_debug("Returning status %d",status); return (status); } #endif /*UTMP_AND_PROC_FIND_USER*/ #else /*not PROC_FIND_USER*/ #ifdef HAVE_UTMPX_H int find_user(char *name, char *tty, char *disp) { struct utmpx *ubuf; int status; struct stat statb; char ftty[20 + sizeof ubuf->ut_line]; char ttyFound[sizeof ubuf->ut_line] = ""; char dispFound[sizeof ubuf->ut_line + 1] = ""; setutxent(); #define SCMPN(a, b) strncmp(a, b, sizeof (a)) status = NOT_HERE; (void) strcpy(ftty, _PATH_DEV); while ((ubuf = getutxent())) { if ((ubuf->ut_type == USER_PROCESS) && (!SCMPN(ubuf->ut_user, name))) { if (*tty == '\0') { /* no particular tty was requested */ (void) strcpy(ftty+5, ubuf->ut_line); if (stat(ftty,&statb) == 0) { if (!(statb.st_mode & 020)) /* ?character device? */ continue; (void) strcpy(ttyFound, ubuf->ut_line); #ifdef USE_UT_HOST (void) strcpy(dispFound, ubuf->ut_host); strcat(dispFound, " "); #endif status = SUCCESS; syslog(LOG_DEBUG, "%s", ttyFound); if ((int) ttyFound[3] > (int) 'f') { #ifdef USE_UT_HOST if (Options.debug_mode) { syslog(LOG_DEBUG, "I wanna this:%s", ttyFound); syslog(LOG_DEBUG, "ut_host=%s", ubuf.ut_host); syslog(LOG_DEBUG, "%s", ubuf.ut_line); } #endif break; } } } else if (!strcmp(ubuf->ut_line, tty)) { status = SUCCESS; break; } } } endutxent(); if (status == SUCCESS) { (void) strcpy(tty, ttyFound); (void) strcpy(disp, dispFound); } return (status); } #else /* HAVE_UTMPX_H */ int find_user(char *name, char *tty, char *disp) { struct utmp ubuf; int status; FILE *fd; struct stat statb; char ftty[20+UT_LINESIZE]; char ttyFound[UT_LINESIZE] = ""; char dispFound[UT_HOSTSIZE+1] = ""; if (!(fd = fopen(_PATH_UTMP, "r"))) { fprintf(stderr, "talkd: can't read %s.\n", _PATH_UTMP); return (FAILED); } #define SCMPN(a, b) strncmp(a, b, sizeof (a)) status = NOT_HERE; (void) strcpy(ftty, _PATH_DEV); while (fread((char *) &ubuf, sizeof ubuf, 1, fd) == 1) { if (!SCMPN(ubuf.ut_name, name)) { if (*tty == '\0') { /* no particular tty was requested */ (void) strcpy(ftty+5, ubuf.ut_line); if (stat(ftty,&statb) == 0) { if (!(statb.st_mode & 020)) /* ?character device? */ continue; (void) strcpy(ttyFound, ubuf.ut_line); #ifdef USE_UT_HOST (void) strcpy(dispFound, ubuf.ut_host); strcat(dispFound, " "); #endif status = SUCCESS; syslog(LOG_DEBUG, "%s", ttyFound); if ((int) ttyFound[3] > (int) 'f') { #ifdef USE_UT_HOST if (Options.debug_mode) { syslog(LOG_DEBUG, "I wanna this:%s", ttyFound); syslog(LOG_DEBUG, "ut_host=%s", ubuf.ut_host); syslog(LOG_DEBUG, "%s", ubuf.ut_line); } #endif break; } } } else if (!strcmp(ubuf.ut_line, tty)) { status = SUCCESS; break; } } } fclose(fd); if (status == SUCCESS) { (void) strcpy(tty, ttyFound); (void) strcpy(disp, dispFound); } return (status); } #endif /* HAVE_UTMPX_H */ #endif /*PROC_FIND_USER*/