/* * Find the UID of the other end of the socket. * This is just a test. * * First, you connect to the horizon server. From the pid of the horizon server * and the file handle, you do * * ls -l /proc/PID/fd/HANDLE * * You end up with a string looking like socket:[number]. Using that number, you lookup * /proc/net/tcp (the number is the inode: 149332706) and you end up with a line like this * 0: 2936A8C0:D8D2 5F35A8C0:1F90 01 00000000:00000000 00:00000000 00000000 0 0 149332706 1 ffff880215b3e900 20 4 28 10 -1 * * 2936A8C0:D8D2 is the from * 5F35A8C0:1F90 is the destination * * So you reverse the from and destination and use this program like this * * chcontext --ctx CTX_OF_THE_VSERVER findproc 5F35A8C0:1F90 2936A8C0:D8D2 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fdpass.h" using namespace std; struct SOCKPROCINFO{ pid_t pid; uid_t uid; gid_t gid; unsigned xid; std::string name; // Program name/path std::string sockid; // socket id in /proc/net/tcp std::string user; // user name running the program std::string group; // group of this user std::string env; // Value of the BKPROJECT variable for this process SOCKPROCINFO(){ pid = (pid_t)-1; uid = (uid_t)-1; gid = (gid_t)-1; xid = 0; } }; static char *str_skipword (char *pt, char **word) { while (isspace(*pt)) pt++; *word = NULL; if (*pt > ' '){ *word = pt; while (*pt > ' ') pt++; if (*pt == ' ') *pt++ = '\0'; } return pt; } static pid_t findpid (const char *socknum) { pid_t ret = (pid_t)-1; string key = string("socket:[") + socknum + "]"; DIR *d = opendir ("/proc"); if (d == NULL){ tlmp_error ("findproc: Can't open dir /proc (%s)\n",strerror(errno)); }else{ struct dirent *e; while (ret == (pid_t)-1 && (e=readdir(d))!=NULL){ if (isdigit(e->d_name[0])){ static string str_proc("/proc/"); static string str_fd ("/fd"); string tmp = str_proc + e->d_name + str_fd; pid_t tmppid = atoi(e->d_name); DIR *sd = opendir (tmp.c_str()); if (sd == NULL){ // A process may vanish while we read, so we only report // error other than ENOENT if (errno != ENOENT) tlmp_error ("findproc: Can't open dir %s (%s)\n",tmp.c_str(),strerror(errno)); }else{ // printf ("DIR %s\n",tmp.c_str()); struct dirent *se; while ((se=readdir(sd))!=NULL){ // printf ("se->d_name %s\n",se->d_name); if (isdigit(se->d_name[0])){ string fhandle = tmp + "/" + se->d_name; char buf[100]; size_t size = readlink(fhandle.c_str(),buf,sizeof(buf)); if (size > 0 && size < sizeof(buf)){ buf[size] = '\0'; // printf ("handle %s buf=%s\n",se->d_name,buf); if (strcmp(buf,key.c_str())==0){ ret = tmppid; break; } } } } closedir(sd); } } } closedir (d); } return ret; } static int finduid (SOCKPROCINFO &info) { glocal int ret = -1; glocal SOCKPROCINFO *info = &info; { char tmp[40]; snprintf (tmp,sizeof(tmp)-1,"/proc/%u/status",info.pid); (tmp,true); int ret = 0; if (strncmp(line,"Uid:",4)==0){ const char *word = str_skip (line+4); glocal.info->uid = atoi(word); }else if (strncmp(line,"Gid:",4)==0){ const char *word = str_skip (line+4); glocal.info->gid = atoi(word); glocal.ret = 0; ret = -1; } return ret; } if (glocal.ret == 0){ char tmp[40]; snprintf (tmp,sizeof(tmp)-1,"/proc/%u/exe",info.pid); char buf[1024]; int len = readlink(tmp,buf,sizeof(buf)-1); if (len > 0){ // If the program has been deleted, we end up with xxxx (deleted) // We turn the spaces into _ for (int i=0; i static int findenv (SOCKPROCINFO &info) { glocal int ret = -1; glocal string *res = &info.env; char tmp[40]; snprintf (tmp,sizeof(tmp)-1,"/proc/%u/environ",info.pid); (tmp,1); int ret = 0; //printf ("filepos %lu recno=%u nbrec=%u\n",filepos,recno,nbrec); const char *pt = (const char *)buf; for (unsigned i=0; i map_user; map map_group; VSERVER(){} }; static void findproc_dump (_F_TCPSERVER_V1 *c, const char *msg, const map &tb) { for (map::const_iterator it=tb.begin(); it != tb.end(); it++){ c->sendf ("\t%s %d %s\n",msg,it->first,it->second.c_str()); } } static string findproc_getname (const map &tb, int id) { map::const_iterator it=tb.find(id); if (it == tb.end()){ char tmp[20]; snprintf (tmp,sizeof(tmp)-1,"%d",id); return tmp; }else{ return it->second; } } static void findproc_sendinfo ( _F_TCPSERVER_V1 *c, const SOCKPROCINFO &info, const map cur_conf) { c->sendf ("pid %d\n",info.pid); c->sendf ("uid %d\n",info.uid); c->sendf ("gid %d\n",info.gid); c->sendf ("xid %u\n",info.xid); if (info.env.size() > 0){ c->sendf ("env %s\n",info.env.c_str()); } c->sendf ("sockid %s\n",info.sockid.c_str()); if (info.user.size() > 0){ c->sendf ("user %s\n",info.user.c_str()); c->sendf ("group %s\n",info.group.c_str()); c->sendf ("name %s\n",info.name.c_str()); }else{ map::const_iterator it = cur_conf.find(info.xid); if (it == cur_conf.end()){ tlmp_error ("Configuration error: No information for XID %d\n",info.xid); c->sendf ("name %s\n",info.name.c_str()); }else{ string user = findproc_getname (it->second.map_user,info.uid); string group = findproc_getname (it->second.map_group,info.gid); c->sendf ("user %s\n",user.c_str()); c->sendf ("group %s\n",group.c_str()); if (it->second.rootdir == "/"){ // A process in the root, no need to remove rootdir c->sendf ("name %s\n",info.name.c_str()); }else{ size_t len = it->second.rootdir.size(); if (strncmp(info.name.c_str(),it->second.rootdir.c_str(),len)==0 && info.name[len] == '/'){ c->sendf ("name %s\n",info.name.c_str()+len); }else{ tlmp_error ("Configuration error: XID=%u Process %s outside rootdir %s\n" ,info.xid,info.name.c_str(),it->second.rootdir.c_str()); c->sendf ("name %s\n",info.name.c_str()); } } } } } int main (int argc, char *argv[]) { glocal int ret = -1; glocal bool daemon = false; glocal const char *pidfile = "/var/run/findproc.pid"; glocal const char *unixpath = "/var/run/findproc.sock"; glocal const char *subunixpath = NULL; glocal bool partial_ok = false; glocal const char *foruser = "blackhole"; glocal.ret = (argc,argv); setproginfo ("findproc",VERSION ,"Find information about processes (path,uid,gid) associated with sockets\n" "Must run in XID 1 to 'see' all vservers\n"); setarg ('d',"daemon","Run in background",glocal.daemon,false); setarg (' ',"pidfile","PID file",glocal.pidfile,false); setarg ('s',"unixsock","Unix socket",glocal.unixpath,false); setarg (' ',"subunixsock","Unix socket of the sub-findproc",glocal.subunixpath,false); setarg (' ',"partialok","Partial result ok",glocal.partial_ok,false); setarg (' ',"foruser","Unix socket usable by this user",glocal.foruser,false); if (glocal.daemon){ syslog (LOG_ERR,"%s",msg); }else{ fprintf (stderr,"%s",msg); } if (glocal.daemon){ syslog (LOG_WARNING,"%s",msg); }else{ fprintf (stderr,"%s",msg); } glocal map new_conf; glocal map cur_conf; int ret = -1; fdpass_checkservice (glocal.unixpath); string tmp = string("unix:") + glocal.unixpath; (tmp.c_str(),1); glocal vector tb; int n = str_splitline (line,' ',glocal.tb); if (n == 5 && glocal.tb[0] == "ident"){ if (glocal.subunixpath != NULL){ // We need 2 findproc processes. One is running as XID 1 // This sub-findproc can see all sockets, but not all // processes. It sees all vserver processes, but not // the root. So the main findproc (running as XID 0), connects // to the sub to retrieve the information. If the information is // incomplete (got the sockid, but not the name of the process // it calls findpid to fill the rest glocal SOCKPROCINFO info; ("unix:",glocal.subunixpath,1); sendf ("ident %s %s %s %s\n",glocal.tb[1].c_str() ,glocal.tb[2].c_str() ,glocal.tb[3].c_str() ,glocal.tb[4].c_str()); if (strcmp(line,"OK")==0){ glocal.ret = 0; end = true; }else if (strncmp(line,"sockid ",7)==0){ glocal.info.sockid = line+7; }else if (strncmp(line,"name ",5)==0){ glocal.info.name = line+5; }else if (strncmp(line,"user ",5)==0){ glocal.info.user = line+5; }else if (strncmp(line,"group ",6)==0){ glocal.info.group = line+6; }else if (strncmp(line,"uid ",4)==0){ glocal.info.uid = atoi(line+4); }else if (strncmp(line,"gid ",4)==0){ glocal.info.gid = atoi(line+4); }else if (strncmp(line,"xid ",4)==0){ glocal.info.xid = atoi(line+4); }else if (strncmp(line,"pid ",4)==0){ glocal.info.pid = atoi(line+4); }else if (strncmp(line,"env ",4)==0){ glocal.info.env = line+4; }else if (strncmp(line,"ERR ",4)==0){ tlmp_error ("Sub-findproc return ERR: %s\n",line+4); end = true; } if (glocal.info.name.size() == 0 && glocal.info.sockid.size() > 0){ pid_t pid = findpid (glocal.info.sockid.c_str()); if (pid != (pid_t)-1){ glocal.info.pid = pid; finduid (glocal.info); findenv (glocal.info); findproc_sendinfo (this,glocal.info,glocal.cur_conf); send ("OK\n"); }else{ tlmp_error ("findproc: Can't find pid associated with socket ID %s\n",glocal.info.sockid.c_str()); sendf ("ERR Can't find information about this connection\n"); } }else{ findproc_sendinfo (this,glocal.info,glocal.cur_conf); send ("OK\n"); } }else{ SOCKPROCINFO info; // /proc/net/tcp may change while we read it. So findproc may fail once in a while. // So we try it twice int ret = -1; for (int i=0; ret == -1 && i<2; i++){ ret = findproc (glocal.tb[1].c_str(),glocal.tb[2].c_str(),glocal.tb[3].c_str(),glocal.tb[4].c_str(),info,glocal.partial_ok); } if (ret == -1){ if (info.sockid.size() > 0){ sendf ("sockid %s\n",info.sockid.c_str()); }else{ sendf ("ERR Can't find information about connection: %s %s -> %s %s\n" ,glocal.tb[1].c_str(),glocal.tb[2].c_str(),glocal.tb[3].c_str(),glocal.tb[4].c_str()); } }else{ findproc_sendinfo (this,info,glocal.cur_conf); } send ("OK\n"); } }else if (n == 1 && glocal.tb[0] == "clear"){ glocal.new_conf.clear(); send ("OK\n"); }else if (n == 3 && glocal.tb[0] == "vserver"){ glocal.new_conf[atoi(glocal.tb[1].c_str())].rootdir = glocal.tb[2]; send ("OK\n"); }else if (n == 4 && glocal.tb[0] == "vuser"){ glocal.new_conf[atoi(glocal.tb[1].c_str())].map_user[atoi(glocal.tb[2].c_str())] = glocal.tb[3]; send ("OK\n"); }else if (n == 4 && glocal.tb[0] == "vgroup"){ glocal.new_conf[atoi(glocal.tb[1].c_str())].map_group[atoi(glocal.tb[2].c_str())] = glocal.tb[3]; send ("OK\n"); }else if (n == 1 && glocal.tb[0] == "commit"){ glocal.cur_conf = glocal.new_conf; send ("OK\n"); }else if (n == 1 && glocal.tb[0] == "status"){ for (map::iterator it = glocal.cur_conf.begin(); it != glocal.cur_conf.end(); it++){ sendf ("XID=%d rootdir=%s\n",it->first,it->second.rootdir.c_str()); } send ("OK\n"); }else if (n == 1 && glocal.tb[0] == "printconf"){ for (map::iterator it = glocal.cur_conf.begin(); it != glocal.cur_conf.end(); it++){ sendf ("XID=%d rootdir=%s\n",it->first,it->second.rootdir.c_str()); findproc_dump (this,"user",it->second.map_user); findproc_dump (this,"group",it->second.map_group); } send ("OK\n"); }else if (n == 1 && glocal.tb[0] == "quit"){ send ("OK\n"); endserver = true; }else{ send ("ERR invalid request\n"); } endclient = true; if (o.is_ok()){ struct passwd *u = getpwnam(glocal.foruser) ; if (u == NULL){ tlmp_error ("Error retrieving %s uid/gid\n",glocal.foruser) ; ret = -1; }else{ chown(glocal.unixpath, u->pw_uid, u->pw_gid) ; chmod(glocal.unixpath, 0770) ; } if (glocal.daemon){ daemon_init (glocal.pidfile,NULL); } o.loop(); ret = 0; } return ret; int ret = -1; if (argc != 4){ fprintf (stderr,"findproc ip_from port_from ip_to port_to\n"); }else{ SOCKPROCINFO info; ret = findproc (argv[0],argv[1],argv[2],argv[3],info,false); if (ret == -1){ fprintf (stderr,"Can't find information about this connection\n"); }else{ printf ("pid=%d uid=%d gid=%d prog=%s\n",info.pid,info.uid,info.gid,info.name.c_str()); } } return 0; return glocal.ret; }