Logo Search packages:      
Sourcecode: sadms version File versions  Download package

remote.py

#!/usr/bin/python
# -*-coding: UTF-8 -*-
# bbou@ac-toulouse.fr
# GPL License
# dim 04 nov 2007 16:57:28 CET   
# remote.py

import pygtk
pygtk.require("2.0")
from gtk import *
import gtk.glade
import sys
import os
import commands
import os.path
import string
import re

import runner

#######################################################################

version='1.0'

sysSetting={
      'hasExpect':'type expect > /dev/null 2>/dev/null && echo true',
      'expectSsh':'expect ssh.expect "%HOST%" "%USER%" "%PASSWORD%" "%COMMAND%"',
      'expectScp':'expect scp.expect "%HOST%" "%USER%" "%PASSWORD%" "%SOURCE%" "%DESTINATION%"',
      'expectAddAgent':'expect keyload.expect "%KEY%" "%PASSWORD%"',
      'expectSshAbortOnPassword':'expect ssh-nopassword.expect "%HOST%" "%USER%" "%COMMAND%"',
      'expectGenKey':'expect keygen.expect "%KEY%" "%PASSWORD%"',
      'flushAgent':'ssh-add -D',
      'listAgent':'ssh-add -L',
      'ssh':'ssh -l %USER% %HOST% "%COMMAND%"',
      'docs':{'default':'file:///usr/share/doc/sshut-%s/%s',
            'redhat':'file:///usr/share/doc/sshut-%s/%s',
            'debian':'file:///usr/share/doc/sshut/%s',
            'suse':'file:///usr/share/doc/packages/sshut/%s',
            'mandriva':'file:///usr/share/doc/sshut-%s/%s'},
      'browser':{'default':'/usr/bin/firefox',
            'redhat':'/usr/bin/firefox',
            'debian':'/usr/bin/firefox',
            'suse':'/usr/bin/firefox',
            'mandriva':'/usr/bin/mozilla-firefox'},

      'getAgentPid':'pidof ssh-agent',
      'deleteKey':'rm -f "%KEY%" "%KEY%.pub"',
      'getAgentVar':'sh -c \'echo ${SSH_AGENT_PID}\'',
      'id':'id -un',
      'mkdir':'mkdir -p "%DIRECTORY%"',
      'catKey':'cat "%KEY%" 2> /dev/null | grep ssh-dss',
      'deleteKnownHosts':'sh -c \'[ -e "$HOME/.ssh/known_hosts" ] && rm "$HOME/.ssh/known_hosts"\'',
      'getDistribution':'sh -c \'if [ -f /etc/debian_version ]; then echo "debian"; else if [ -f /etc/SuSE-release ]; then echo "suse"; else if [ -e /etc/mandrake-release -o -e /etc/mandriva-release ]; then echo "mandriva"; else if [ -e /etc/redhat-release ]; then echo "redhat"; else echo "default"; fi; fi; fi; fi\'',
}

def sshDo(remoteHost,remoteUser,remoteDir,command):
      cl=sysSetting['expectSshAbortOnPassword']
      cl=cl.replace('%HOST%',remoteHost)
      cl=cl.replace('%USER%',remoteUser)
      cl=cl.replace('%DIRECTORY%',remoteDir)
      cl=cl.replace('%COMMAND%',command)
      return os.system(cl)==0

#######################################################################
#     KeyView
#######################################################################

class KeyView:
      
      IMAGE=0
      ENCRYPTIONTEXT=1
      SUBJECTTEXT=2
      DATATEXT=3

      pixbufs=[]

      def __init__(self,listview):

            self.listview=listview

            # header
            self.listview.set_headers_visible(True)

            # model
            self.model=ListStore(gtk.gdk.Pixbuf,str,str,str)
            self.listview.set_model(self.model)

            # columns
            column=gtk.TreeViewColumn('')
            cell=gtk.CellRendererPixbuf()
            column.pack_start(cell,True)
            column.set_cell_data_func(cell,self.render)
            column.add_attribute(cell,'pixbuf',KeyView.IMAGE)
            self.listview.append_column(column)

            column=gtk.TreeViewColumn('type')
            cell=gtk.CellRendererText()
            column.pack_start(cell,True)
            column.set_cell_data_func(cell,self.render)
            column.add_attribute(cell,'text',KeyView.ENCRYPTIONTEXT)
            self.listview.append_column(column)
            
            column=gtk.TreeViewColumn('subject')
            cell=gtk.CellRendererText()
            column.pack_start(cell,True)
            column.set_cell_data_func(cell,self.render)
            column.add_attribute(cell,'text',KeyView.SUBJECTTEXT)
            self.listview.append_column(column)
            
            column=gtk.TreeViewColumn('data')
            cell=gtk.CellRendererText()
            column.pack_start(cell,True)
            column.set_cell_data_func(cell,self.render)
            column.add_attribute(cell,'text',KeyView.DATATEXT)
            self.listview.append_column(column)
            
            # images
            if KeyView.pixbufs==[]:
                  KeyView.pixbufs=self.setupImages()
            return

      def setupImages(self):
            imageFile=['keyring.png']
            pixbufs=[]
            for i in range(len(imageFile)):
                  pixbufs.append(gtk.gdk.pixbuf_new_from_file('pixmaps/'+imageFile[i]))
            return pixbufs

      # render callback
      def render(self,column,cell,model,i):
            r=model.get_path(i)
            toggle=r[0] % 2 == 0
            if toggle:
                  cell.set_property('cell-background','lightGray')
            else:
                  cell.set_property('cell-background','white')
            return

      def find(self,what,field):
            for s in self.model:
                  if s[field]==what:
                        return True
            return False
      
      def find2(self,what1,field1,what2,field2):
            for s in self.model:
                  if s[field1]==what1 and s[field2]==what2:
                        return True
            return False
      
      def find3(self,what1,field1,what2,field2,what3,field3):
            for s in self.model:
                  if s[field1]==what1 and s[field2]==what2 and s[field3]==what3:
                        return True
            return False
      
      def get(self,which,field):
            return self.model[which][field]
      
      def clear(self):
            self.model.clear()
            return
      
      def set(self,keys):
            for k in keys:
                  if k=='':
                        continue
                  #print 'key=<%s>' % (k)
                  encryptiontype,data,subject=k.split(' ')
                  self.model.append([KeyView.pixbufs[0],encryptiontype,subject,data])
            return
                  
#######################################################################
#     RemoteManager
#######################################################################

class RemoteManager:
      
      pixbufs=[]
      OK=0
      ERROR=1
      NONE=2

      KEY=0
      REMOTEKEY=1
      AGENT=2
      
      def __init__(self):
            handlers={
                  'on_help_clicked':            self.help,
                  'on_generateKey_clicked':     self.generateKey,
                  'on_deleteKey_clicked':       self.deleteKey,
                  'on_browseKey_clicked':       self.browseKey,
                  'on_loadAgent_clicked':       self.loadAgent,
                  'on_flushAgent_clicked':      self.flushAgent,
                  'on_exportKey_clicked':       self.exportKey,
                  'on_getExportedKey_clicked':  self.getExportedKey,
                  'on_invalidatePasswords_clicked':self.invalidatePasswords,
                  'on_run_clicked':       self.runSsh,
                  'on_keyPathEntry_changed':    self.forceRefresh,
            }

            self.keyPasswordCache={}
            self.sshPasswordCache={}

            self.widgets=gtk.glade.XML('remote.glade')
            self.widgets.signal_autoconnect(handlers)
            self.dialog=self['remote']

            self.keyPasswordDialog=self['keyPasswordDialog']
            self.keyPasswordEntry=self['keyPasswordEntry']
            self.keyPasswordKeyLabel=self['keyPasswordKeyLabel']
            self.sshPasswordDialog=self['sshPasswordDialog']
            self.sshPasswordEntry=self['sshPasswordEntry']
            self.sshPasswordHostLabel=self['sshPasswordHostLabel']
            self.sshPasswordUserLabel=self['sshPasswordUserLabel']

            self.remoteCheck=self['remoteCheck']
            self.hostEntry=self['hostEntry']
            self.userEntry=self['userEntry']
            self.dirEntry=self['dirEntry']
            self.keyPathEntry=self['keyPathEntry']

            self.keyView=KeyView(self['keyView'])
            self.agentView=KeyView(self['agentView'])
            self.remoteKeyView=KeyView(self['remoteKeyView'])

            self.runCommand=self['runCommandEntry']
            self.runView=self['runOutputView']
            self.runBuffer=gtk.TextBuffer(None)
            self.runView.set_buffer(self.runBuffer)

            self.settingsImage=self['settingsImage']
            self.keyImage=self['keyImage']
            self.agentImage=self['agentImage']
            self.remoteKeyImage=self['remoteKeyImage']

            # images
            if RemoteManager.pixbufs==[]:
                  RemoteManager.pixbufs=self.setupImages()
            # status
            self.status={
                  RemoteManager.KEY:RemoteManager.NONE,
                  RemoteManager.REMOTEKEY:RemoteManager.NONE,
                  RemoteManager.AGENT:RemoteManager.NONE
            }
            self.statusImage={
                  RemoteManager.KEY:self.keyImage,
                  RemoteManager.REMOTEKEY:self.remoteKeyImage,
                  RemoteManager.AGENT:self.agentImage
            }
            return

      def setupImages(self):
            imageFile=['green.png','red.png','grey.png']
            pixbufs=[]
            for i in range(len(imageFile)):
                  pixbufs.append(gtk.gdk.pixbuf_new_from_file('pixmaps/'+imageFile[i]))
            return pixbufs

      # termination

      def exit(self,*options):
            if __name__ == "__main__":
                  gtk.main_quit()
            else:
                  self.dialog.destroy()   
            return
                        
      # widget access

      def __getitem__(self, key):
            return self.widgets.get_widget(key)

      # P A S S W O R D

      def getKeyPass(self,key):
            if self.keyPasswordCache.has_key(key):
                  password,valid=self.keyPasswordCache[key]
                  if valid:
                        return password
                  self.keyPasswordEntry.set_text(password)
            self.keyPasswordKeyLabel.set_text(key)
            response=self.keyPasswordDialog.run()
            self.keyPasswordDialog.hide()
            if response==gtk.RESPONSE_OK:
                  password=self.keyPasswordEntry.get_text()
                  self.keyPasswordCache[key]=[password,True]
                  self.keyPasswordDialog.hide()
                  return password
            self.keyPasswordDialog.hide()
            return ''

      def getSshPass(self,host,user):
            key=(host,user)
            if self.sshPasswordCache.has_key(key):
                  password,valid=self.sshPasswordCache[key]
                  if valid:
                        return password
                  self.sshPasswordEntry.set_text(password)
            self.sshPasswordHostLabel.set_text(host)
            self.sshPasswordUserLabel.set_text(user)
            response=self.sshPasswordDialog.run()
            self.sshPasswordDialog.hide()
            if response==gtk.RESPONSE_OK:
                  password=self.sshPasswordEntry.get_text()
                  self.sshPasswordCache[key]=[password,True]
                  self.sshPasswordDialog.hide()
                  return password
            self.sshPasswordDialog.hide()
            return ''

      def hasSshPass(self,host,user):
            key=(host,user)
            if self.sshPasswordCache.has_key(key):
                  return True
            return False

      def invalidatePasswords(self,*options):
            for k in self.keyPasswordCache.keys():
                  self.keyPasswordCache[k][1]=False

            for k in self.sshPasswordCache.keys():
                  self.sshPasswordCache[k][1]=False
            return

      # K E Y

      def browseKey(self,*options):
            dialog=gtk.FileChooserDialog("Open file...",None,gtk.FILE_CHOOSER_ACTION_OPEN,(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
            dialog.set_default_response(gtk.RESPONSE_OK)
            if os.path.exists('~/.ssh'):
                  dialog.set_current_folder('~/.ssh')
            filter=gtk.FileFilter()
            filter.set_name("All files")
            filter.add_pattern("*")
            dialog.add_filter(filter)
            response = dialog.run()
            if response == gtk.RESPONSE_OK:
                  self.keyPathEntry.set_text(dialog.get_filename())
                  self.refresh()
            elif response == gtk.RESPONSE_CANCEL:
                  pass
            dialog.destroy()
            return

      def generateKey(self,*options):
            keyFile=self.getKeyFile()
            cl=sysSetting['expectGenKey']
            cl=cl.replace('%KEY%',keyFile)
            cl=cl.replace('%PASSWORD%',self.getKeyPass(keyFile))
            status,output=self.runToString(cl)
            self.refresh()
            return

      def deleteKey(self,*options):
            key=self.getKeyFile()
            cl=sysSetting['deleteKey']
            cl=cl.replace('%KEY%',key)
            status,output=self.runToString(cl)
            self.refresh()
            return

      # A G E N T
      
      def loadAgent(self,*options):
      
            # agent is running
            running,message=self.agentRunning()
            if not running:
                  self.putMessage(message)
                  return      
            
            # flush
            #self.flushAgent()
            
            # load
            keyFile=self.getKeyFile()
            cl=sysSetting['expectAddAgent']
            cl=cl.replace('%KEY%',keyFile)
            cl=cl.replace('%PASSWORD%',self.getKeyPass(keyFile))
            status,output=self.runToString(cl)
            self.refreshAgentPage(True)   
            return

      def flushAgent(self,*options):
            cl=sysSetting['flushAgent']
            status,output=self.runToString(cl)
            self.refreshAgentPage(False)  
            return

      def putMessage(self,message):
            dialog=gtk.MessageDialog(None,gtk.DIALOG_MODAL,gtk.MESSAGE_WARNING,gtk.BUTTONS_CLOSE,message)
            dialog.run()
            dialog.destroy()
            return

      # R E M O T E   K E Y

      def exportKey(self,*options):
            sshdir='~/.ssh'
            host=self.hostEntry.get_text()
            user=self.userEntry.get_text()

            cl2=sysSetting['mkdir']
            cl2=cl2.replace('%DIRECTORY%',sshdir)
            cl=sysSetting['expectSsh']
            cl=cl.replace('%HOST%',host)
            cl=cl.replace('%USER%',user)
            cl=cl.replace('%PASSWORD%',self.getSshPass(host,user))
            cl=cl.replace('%COMMAND%',cl2)
            status,output=self.runToString(cl)

            cl=sysSetting['expectScp']
            cl=cl.replace('%HOST%',host)
            cl=cl.replace('%USER%',user)
            cl=cl.replace('%PASSWORD%',self.getSshPass(host,user))
            cl=cl.replace('%SOURCE%',self.getKeyFile()+'.pub')
            cl=cl.replace('%DESTINATION%','~/.ssh/authorized_keys')
            status,output=self.runToString(cl)
            self.refreshRemoteKeyPage()
            return

      def getExportedKey(self,*options):
            self.refreshRemoteKeyPage()
            return

      # D I S P L A Y   U P D A T E S

      def refreshKeyPage(self):
            self.keyView.clear()
            keyFile=self.getKeyFile()+'.pub'
            keys=self.getKeys(keyFile)
            if keys!=[]:
                  self.keyView.set(keys)
                  self.setStatus(RemoteManager.KEY,RemoteManager.OK)
                  return True
            self.setStatus(RemoteManager.KEY,RemoteManager.ERROR)
            return False

      def refreshRemoteKeyPage(self):
            self.remoteKeyView.clear()
            sshdir='~/.ssh'
            host=self.hostEntry.get_text()
            user=self.userEntry.get_text()
            cl2=sysSetting['catKey']
            cl2=cl2.replace('%DIRECTORY%',sshdir)
            cl2=cl2.replace('%KEY%','.ssh/authorized_keys')
            cl=sysSetting['expectSsh']
            cl=cl.replace('%HOST%',host)
            cl=cl.replace('%USER%',user)
            cl=cl.replace('%PASSWORD%',self.getSshPass(host,user))
            cl=cl.replace('%COMMAND%',cl2)
            status,output=self.runToString(cl)
            if status==0:
                  keys=[]
                  if output !='':
                        keys=output.splitlines()
                  keys=[k for k in keys if re.match('^ssh-dss',k)]
                  self.remoteKeyView.set(keys)
                  if self.matchRemoteKeys(keys):
                        self.setStatus(RemoteManager.REMOTEKEY,RemoteManager.OK)
                        return True             
            self.setStatus(RemoteManager.REMOTEKEY,RemoteManager.ERROR)
            return False

      def agentRunning(self):
            # agent is running
            var=self.runToString(sysSetting['getAgentVar'])
            if var=='':
                  return False,"SSH agent didn't start (no variable),\nRestart your X-session once the keys are generated"
            status,pid=self.runToString(sysSetting['getAgentPid'])
            if pid=='':
                  return False,"SSH agent didn't start (no pid),\nRestart your X-session once the keys are generated"
            return True, "ok"

      def refreshAgentPage(self,verbose):
            self.agentView.clear()
            # agent is running
            running,message=self.agentRunning()
            if not running:
                  return False 
            
            cl=sysSetting['listAgent']
            status,output=self.runToString(cl)
            if status==0:
                  keys=[]
                  if output !='':
                        keys=output.splitlines()
                  self.agentView.set(keys)
                  if self.matchAgentKeys(keys):
                        self.setStatus(RemoteManager.AGENT,RemoteManager.OK)
                        return True
            else:
                  if verbose:
                        self.putMessage(output)
            self.setStatus(RemoteManager.AGENT,RemoteManager.ERROR)
            return False

      def forceRefresh(self,*options):
            self.refresh()
            return

      def refresh(self):
            self.refreshKeyPage()
            self.refreshAgentPage(False)

            host=self.hostEntry.get_text()
            user=self.userEntry.get_text()
            if self.hasSshPass(host,user):
                  self.refreshRemoteKeyPage()
            return

      def matchRemoteKeys(self,keys):
            for k in keys:
                  encryption,data,subject=k.split()
                  if self.keyView.find3(data,KeyView.DATATEXT,encryption,KeyView.ENCRYPTIONTEXT,subject,KeyView.SUBJECTTEXT):
                        return True
            return False
                  
      def matchAgentKeys(self,keys):
            keyFile=os.path.expanduser(os.path.expandvars(self.keyPathEntry.get_text()))+".pub"
            if not os.path.exists(keyFile):
                  return False
            keys0=self.getKeys(keyFile)
            if keys0!=[]:
                  for k0 in keys0:
                        encryption0,data0,subject0=k0.split()
                        for k in keys:
                              encryption,data,subject=k.split()
                              if subject0==subject and encryption0==encryption and data0==data:
                                    return True
            return False
                  
      def setStatus(self,domain,value):

            # local
            self.status[domain]=value
            self.statusImage[domain].set_from_pixbuf(RemoteManager.pixbufs[value])

            # global
            globalValue=RemoteManager.OK
            for k in self.status.keys():
                  l=self.status[k]
                  if l==RemoteManager.NONE:
                        globalValue=RemoteManager.NONE
                        continue
                  elif l==RemoteManager.ERROR:
                        globalValue=RemoteManager.ERROR
                        break
                  elif l==RemoteManager.OK:
                        continue

            self.settingsImage.set_from_pixbuf(RemoteManager.pixbufs[globalValue])
            return

      def canRun(self):
            return self.status[RemoteManager.KEY]==RemoteManager.OK and self.status[RemoteManager.AGENT]==RemoteManager.OK

      def help(self,*options):
            distribution=self.getDistribution()
            browser=sysSetting['browser'][distribution]
            urlbase=sysSetting['docs'][distribution]
            if distribution=="suse" or distribution=="debian":
                  url=urlbase % ('remote.html')
            else:
                  url=urlbase % (self.getVersion(),'remote.html')
            os.spawnv(os.P_NOWAIT,browser,[browser,url])
            return

      def getVersion(self):
            return version

      def about(self,*options):
            self.aboutDialog.show()
            return

      # H E L P E R S

      def hasExpect(self):
            cl=sysSetting['hasExpect']
            status,output=self.runToString(cl)
            if status:
                  return False
            return output.strip('\n\r ')=='true'

      def getId(self):
            cl=sysSetting['id']
            status,output=self.runToString(cl)
            if status:
                  return False
            return output.strip('\n\r ')

      def getKeys(self,filename):
            if not os.path.exists(filename):
                  return []
            f=open(filename,'r')
            lines=f.read()
            f.close()
            return lines.strip('\n\r ').splitlines()

      def getKeyFile(self):
            path=self.keyPathEntry.get_text()
            path=os.path.expanduser(path)
            path=os.path.expandvars(path)
            return path

      def getDistribution(self):
            cl=sysSetting['getDistribution']
            status,output=self.runToString(cl)
            if status:
                  return output.strip('\n\r ')
            return 'default'

      def runSsh(self,*options):
            self.runBuffer.delete(self.runBuffer.get_start_iter(),self.runBuffer.get_end_iter())
            host=self.hostEntry.get_text()
            user=self.userEntry.get_text()
            wdir=self.dirEntry.get_text()
            cl2=self.runCommand.get_text()
            cl=sysSetting['expectSshAbortOnPassword']
            cl=cl.replace('%HOST%',host)
            cl=cl.replace('%USER%',user)
            cl=cl.replace('%DIRECTORY%',wdir)
            cl=cl.replace('%COMMAND%',cl2)
            status,output=self.runToString(cl)
            if status==0:
                  self.runBuffer.insert(self.runBuffer.get_start_iter(),output+'\n')
            else:
                  self.runBuffer.insert(self.runBuffer.get_start_iter(),'<%s> failed to run through SSH\n[error=%s]\n' % (cl2,status))
            return

      def makeRunner(self):
            remote=m.remoteCheck.get_active()
            host= m.hostEntry.get_text()
            user=m.userEntry.get_text()
            wdir=m.dirEntry.get_text()
            runner=runner.Runner()
            if runRemote and host != "" and user != "" and wdir != "":
                  resultRunner.setRemote(host,user,wdir)
            elif not runRemote and host != "" and user != "" and wdir != "":
                  resultRunner.setLocal(host,user,wdir)
            else:
                  resultRunner.setLocal()             
            return runner

      # L O C A L  R U N N E R

      def runToString(self,cl):
            #print '>',cl
            status,output=commands.getstatusoutput(cl)            
            if status!=0:
                  print '%s failed w/ exit code %d' %(cl,status)
            #print '<',status,output
            return status,output.strip('\n\r ')

#######################################################################
#     MAIN
#######################################################################

if __name__ == "__main__":
      e=sys.argv[0]
      e=os.path.realpath(e)
      d=os.path.dirname(e)
      os.chdir(d)
      m=RemoteManager()
      m.dialog.connect('destroy',m.exit)
      m['okButton'].connect('clicked',m.exit)
      m['cancelButton'].connect('clicked',m.exit)

      if  not m.hasExpect():
                  m.putMessage("Expect package is needed if your settings are to tested")

      if len(sys.argv)>1:
            m.hostEntry.set_text(sys.argv[1])
            m.userEntry.set_text(sys.argv[2])
            m.dirEntry.set_text(sys.argv[3])
            m.keyPathEntry.set_text('~/.ssh/id_dsa')
            m.refresh()
      else:
            
            m.hostEntry.set_text('localhost')
            m.userEntry.set_text(m.getId())
            m.dirEntry.set_text('~')
            m.keyPathEntry.set_text('~/.ssh/id_dsa')
            m.refresh()
      gtk.main()

Generated by  Doxygen 1.6.0   Back to index