public inbox for cluster-cvs@sourceware.org
help / color / mirror / Atom feed
* cluster: RHEL5 - Resolves: #498983 - fence_apc_snmp affected by symlink attack vulnerabilities
@ 2009-06-26 10:43 Marek Grác
  0 siblings, 0 replies; only message in thread
From: Marek Grác @ 2009-06-26 10:43 UTC (permalink / raw)
  To: cluster-cvs-relay

Gitweb:        http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=fcbbc9d89f025bf4d0090becdb8494800af19209
Commit:        fcbbc9d89f025bf4d0090becdb8494800af19209
Parent:        6fbdf268d92e2d3567f452c9e92e3898b58564ac
Author:        Marek 'marx' Grac <mgrac@redhat.com>
AuthorDate:    Fri Jun 26 12:37:35 2009 +0200
Committer:     Marek 'marx' Grac <mgrac@redhat.com>
CommitterDate: Fri Jun 26 12:37:35 2009 +0200

Resolves: #498983 - fence_apc_snmp affected by symlink attack vulnerabilities

New fence agent based on snmp fencing library was used. Agent was already deployed in
STABLE3 branch.
---
 fence/agents/apc_snmp/fence_apc_snmp.py |  624 +++++++++----------------------
 1 files changed, 177 insertions(+), 447 deletions(-)

diff --git a/fence/agents/apc_snmp/fence_apc_snmp.py b/fence/agents/apc_snmp/fence_apc_snmp.py
old mode 100755
new mode 100644
index fb79054..df9ecea
--- a/fence/agents/apc_snmp/fence_apc_snmp.py
+++ b/fence/agents/apc_snmp/fence_apc_snmp.py
@@ -1,462 +1,192 @@
 #!/usr/bin/python
 
-#############################################################################
-#############################################################################
-##
-##  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-##  Copyright (C) 2004-2007 Red Hat, Inc.  All rights reserved.
-##
-##  This copyrighted material is made available to anyone wishing to use,
-##  modify, copy, or redistribute it subject to the terms and conditions
-##  of the GNU General Public License v.2.
-##
-#############################################################################
-## This APC Fence script uses snmp to control the APC power
-## switch. This script requires that net-snmp-utils be installed
-## on all nodes in the cluster, and that the powernet369.mib file be
-## located in /usr/share/snmp/mibs/
-#############################################################################
-#############################################################################
-
-
-
-import getopt, sys
-import os
-import datetime
-import select
-import signal
-from glob import glob
+# The Following agent has been tested on:
+# - APC Switched Rack PDU (MB:v3.7.0 PF:v2.7.0 PN:apc_hw02_aos_270.bin AF1:v2.7.3 AN1:apc_hw02_aos_270.bin
+#    AF1:v2.7.3 AN1:apc_hw02_rpdu_273.bin MN:AP7930 HR:B2) - SNMP v1
+# - APC Web/SNMP Management Card (MB:v3.8.6 PF:v3.5.8 PN:apc_hw02_aos_358.bin AF1:v3.5.7 AN1:apc_hw02_aos_358.bin
+#    AF1:v3.5.7 AN1:apc_hw02_rpdu_357.bin MN:AP7900 HR:B2) - SNMP v1 and v3 (noAuthNoPrivacy,authNoPrivacy, authPrivacy)
+# - APC Switched Rack PDU (MB:v3.7.0 PF:v2.7.0 PN:apc_hw02_aos_270.bin AF1:v2.7.3 AN1:apc_hw02_rpdu_273.bin
+#    MN:AP7951 HR:B2) - SNMP v1
+
+import sys, re, pexpect
+sys.path.append("@FENCEAGENTSLIBDIR@")
+from fencing import *
+from fencing_snmp import *
 
 #BEGIN_VERSION_GENERATION
-FENCE_RELEASE_NAME=""
+RELEASE_VERSION="APC SNMP fence agent"
 REDHAT_COPYRIGHT=""
 BUILD_DATE=""
 #END_VERSION_GENERATION
 
-POWER_ON="outletOn"
-POWER_OFF="outletOff"
-POWER_REBOOT="outletReboot"
-
-
-# oid defining fence device 
-oid_sysObjectID = '.1.3.6.1.2.1.1.2.0'
-
-class SNMP:
-	def __init__(self, params):
-		self.hostname  = params['ipaddr']
-		self.udpport   = params['udpport']
-		self.community = params['community']
-	
-	def get(self, oid):
-		args = ['/usr/bin/snmpget']
-		args.append('-Oqn')
-		args.append('-v')
-		args.append('1')
-		args.append('-c')
-		args.append(self.community)
-		args.append('-m')
-		args.append('ALL')
-		args.append(self.hostname + ':' + self.udpport)
-		args.append(oid)
-		strr, code = execWithCaptureStatus("/usr/bin/snmpget", args)
-		if code:
-			raise Exception, 'snmpget failed'
-		l = strr.strip().split()
-		return l[0], ' '.join(l[1:])
-	
-	def set_int(self, oid, value):
-		args = ['/usr/bin/snmpset']
-		args.append('-Oqn')
-		args.append('-v')
-		args.append('1')
-		args.append('-c')
-		args.append(self.community)
-		args.append('-m')
-		args.append('ALL')
-		args.append(self.hostname + ':' + self.udpport)
-		args.append(oid)
-		args.append('i')
-		args.append(str(value))
-		strr,code = execWithCaptureStatus("/usr/bin/snmpset", args)
-		if code:
-			raise Exception, 'snmpset failed'
-		
-	def walk(self, oid):
-		args = ['/usr/bin/snmpwalk']
-		args.append('-Oqn')
-		args.append('-v')
-		args.append('1')
-		args.append('-c')
-		args.append(self.community)
-		args.append('-m')
-		args.append('ALL')
-		args.append(self.hostname + ':' + self.udpport)
-		args.append(oid)
-		strr,code = execWithCaptureStatus("/usr/bin/snmpwalk", args)
-		if code:
-			raise Exception, 'snmpwalk failed'
-		lines = strr.strip().splitlines()
-		ret = []
-		for line in lines:
-			l = line.strip().split()
-			ret.append((l[0], ' '.join(l[1:]).strip('"')))
-		return ret
-	
-
-
-class FenceAgent:
-	
-	def __init__(self, params):
-	   self.snmp = SNMP(params)
-	
-	def resolve_outlet(self):
-		raise Exception, 'resolve_outlet() not implemented'
-	
-	def status(self):
-		oid = self.status_oid % self.resolve_outlet()
-		dummy, stat = self.snmp.get(oid)
-		if stat == self.state_on or stat == "outletStatusOn":
-			return 'on'
-		elif stat == self.state_off or stat == "outletStatusOff":
-			return 'off'
-		else:
-			raise Exception, 'invalid status ' + stat
-	
-	def power_off(self):
-		oid = self.control_oid % self.resolve_outlet()
-		self.snmp.set_int(oid, self.turn_off)
-	
-	def power_on(self):
-		oid = self.control_oid % self.resolve_outlet()
-		self.snmp.set_int(oid, self.turn_on)
-	
-
-
-
-		
-
-		
-
-class MasterSwitch(FenceAgent):
-	
-	def __init__(self, params):
-	   FenceAgent.__init__(self, params)
-	   
-	   self.status_oid       = '.1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.%s'
-	   self.control_oid      = '.1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.%s'
-	   self.outlet_table_oid = '.1.3.6.1.4.1.318.1.1.12.3.5.1.1.2'
-	   
-	   self.state_on  = '1'
-	   self.state_off = '2'
-	   
-	   self.turn_on   = '1'
-	   self.turn_off  = '2'
-	   
-	   self.port = params['port']
-	
-	def resolve_outlet(self):
-		outlet = None
-		try:
-			outlet = str(int(self.port))
-		except:
-			table = self.snmp.walk(self.outlet_table_oid)
-			for row in table:
-				if row[1] == self.port:
-					t = row[0].strip().split('.')
-					outlet = t[len(t)-1]
-		if outlet == None:
-			raise Exception, 'unable to resolve ' + self.port
-		else:
-			self.port = outlet
-		return outlet
-	
-	
-class MasterSwitchPlus(FenceAgent):
-	def __init__(self, params):
-	   FenceAgent.__init__(self, params)
-	   
-	   self.status_oid       = '.1.3.6.1.4.1.318.1.1.6.7.1.1.5.%s.1.%s'
-	   self.control_oid      = '.1.3.6.1.4.1.318.1.1.6.5.1.1.5.%s.1.%s'
-	   self.outlet_table_oid = '.1.3.6.1.4.1.318.1.1.6.7.1.1.4'
-	   
-	   self.state_on  = '1'
-	   self.state_off = '2'
-	   
-	   self.turn_on   = '1'
-	   self.turn_off  = '3'
-	   
-	   try:
-		   self.switch = params['switch']
-	   except:
-		   self.switch = ''
-	   self.port   = params['port']
-   
-	def resolve_outlet(self):
-		switch = None
-		outlet = None
-		try:
-			switch = str(int(self.switch))
-			outlet = str(int(self.port))
-		except:
-			table = self.snmp.walk(self.outlet_table_oid)
-			for row in table:
-				if row[1] == self.port:
-					t = row[0].strip().split('.')
-					outlet = t[len(t)-1]
-					switch = t[len(t)-3]
-		if outlet == None:
-			raise Exception, 'unable to resolve ' + self.port
-		else:
-			self.switch = switch
-			self.port   = outlet
-		return (switch, outlet)
-	
-
-
-
-	
-
-def usage():
-        print "Usage:"
-        print ""
-        print "Options:"
-        print "  -h               Usage"
-        print "  -a <ip>          IP address or hostname of fence device"
-        print "  -u <udpport>     UDP port to use (default 161)"
-        print "  -c <community>   SNMP community (default 'private')"
-        print "  -n <num>         Outlet name/number to act on"
-        print "  -o <string>      Action: Reboot (default), On, Off and Status"
-        print "  -v <filename>    Verbose mode - write to file"
-        print "  -V               Version"
-	
-        sys.exit(0)
-
-
-
-file_log = None
-def set_logging(verbose, verbose_filename):
-	global file_log
-	if verbose:
-		file_log = open(verbose_filename, 'a')
-		file_log.write('\n-----------  ')
-		file_log.write(datetime.datetime.today().ctime())
-		file_log.write('  -----------\n')
-def log(msg, error=False):
-	global file_log
-	if msg.rfind('\n') != len(msg)-1:
-		msg += '\n'
-	if file_log != None:
-		file_log.write(msg)
-	if error:
-		o = sys.stderr
+### CONSTANTS ###
+# oid defining fence device
+OID_SYS_OBJECT_ID='.1.3.6.1.2.1.1.2.0'
+
+### GLOBAL VARIABLES ###
+# Device - see ApcRPDU, ApcMSP, ApcMS
+device=None
+
+# Port ID
+port_id=None
+# Switch ID
+switch_id=None
+
+# Classes describing Device params
+class ApcRPDU:
+	# Rack PDU
+	status_oid=      '.1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.%d'
+	control_oid=     '.1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.%d'
+	outlet_table_oid='.1.3.6.1.4.1.318.1.1.12.3.5.1.1.2'
+	ident_str="APC rPDU"
+	state_on=1
+	state_off=2
+	turn_on=1
+	turn_off=2
+	has_switches=False
+
+class ApcMSP:
+	# Master Switch+
+	status_oid=      '.1.3.6.1.4.1.318.1.1.6.7.1.1.5.%d.1.%d'
+	control_oid=     '.1.3.6.1.4.1.318.1.1.6.5.1.1.5.%d.1.%d'
+	outlet_table_oid='.1.3.6.1.4.1.318.1.1.6.7.1.1.4'
+	ident_str="APC Master Switch+"
+	state_on=1
+	state_off=2
+	turn_on=1
+	turn_off=3
+	has_switches=True
+
+class ApcMS:
+	# Master Switch - seems oldest, but supported on every APC PDU
+	status_oid=      '.1.3.6.1.4.1.318.1.1.4.4.2.1.3.%d'
+	control_oid=     '.1.3.6.1.4.1.318.1.1.4.4.2.1.3.%d'
+	outlet_table_oid='.1.3.6.1.4.1.318.1.1.4.4.2.1.4'
+	ident_str="APC Master Switch (fallback)"
+	state_on=1
+	state_off=2
+	turn_on=1
+	turn_off=2
+	has_switches=False
+
+### FUNCTIONS ###
+def apc_set_device(conn,options):
+	global device
+
+	agents_dir={'.1.3.6.1.4.1.318.1.3.4.5':ApcRPDU,
+		    '.1.3.6.1.4.1.318.1.3.4.4':ApcMSP,
+		    None:ApcMS}
+
+	# First resolve type of APC
+	apc_type=conn.walk(OID_SYS_OBJECT_ID)
+
+	if (not ((len(apc_type)==1) and (agents_dir.has_key(apc_type[0][1])))):
+		apc_type=[[None,None]]
+
+	device=agents_dir[apc_type[0][1]]
+
+	conn.log_command("Trying %s"%(device.ident_str))
+
+def apc_resolv_port_id(conn,options):
+	global port_id,switch_id,device
+
+	if (device==None):
+		apc_set_device(conn,options)
+
+	# Now we resolv port_id/switch_id
+	if ((options["-n"].isdigit()) and ((not device.has_switches) or (options["-s"].isdigit()))):
+		port_id=int(options["-n"])
+
+		if (device.has_switches):
+			switch_id=int(options["-s"])
 	else:
-		o = sys.stdout
-	o.write(msg)
+		table=conn.walk(device.outlet_table_oid,30)
 
+		for x in table:
+			if (x[1].strip('"')==options["-n"]):
+				t=x[0].split('.')
+				if (device.has_switches):
+					port_id=int(t[len(t)-1])
+					switch_id=int(t[len(t)-3])
+				else:
+					port_id=int(t[len(t)-1])
 
+	if (port_id==None):
+		fail_usage("Can't find port with name %s!"%(options["-n"]))
 
+def get_power_status(conn,options):
+	global port_id,switch_id,device
+
+	if (port_id==None):
+		apc_resolv_port_id(conn,options)
+
+	oid=((device.has_switches) and device.status_oid%(switch_id,port_id) or device.status_oid%(port_id))
+
+	(oid,status)=conn.get(oid)
+	return (status==str(device.state_on) and "on" or "off")
+
+def set_power_status(conn, options):
+	global port_id,switch_id,device
+
+	if (port_id==None):
+		apc_resolv_port_id(conn,options)
+
+	oid=((device.has_switches) and device.control_oid%(switch_id,port_id) or device.control_oid%(port_id))
+
+	conn.set(oid,(options["-o"]=="on" and device.turn_on or device.turn_off))
+
+
+def get_outlets_status(conn, options):
+	global device
+
+	result={}
+
+	if (device==None):
+		apc_set_device(conn,options)
+
+	res_ports=conn.walk(device.outlet_table_oid,30)
+
+	for x in res_ports:
+		t=x[0].split('.')
+
+		port_num=((device.has_switches) and "%s:%s"%(t[len(t)-3],t[len(t)-1]) or "%s"%(t[len(t)-1]))
+
+                port_name=x[1].strip('"')
+                port_status=""
+                result[port_num]=(port_name,port_status)
+
+        return result
+
+# Define new options
+def apc_snmp_define_defaults():
+	all_opt["snmp_version"]["default"]="1"
+	all_opt["community"]["default"]="private"
+
+# Main agent method
 def main():
-	try:
-		main2()
-		return 0
-	except Exception, e:
-		log(str(e), True)
-		sys.exit(1)
-def main2():
-  
-  agents_dir = {'.1.3.6.1.4.1.318.1.3.4.5' : MasterSwitch,
-		'.1.3.6.1.4.1.318.1.3.4.4' : MasterSwitchPlus}
-  
-  verbose = False
-  params = {}
-  
-  if len(sys.argv) > 1:
-    try:
-      opts, args = getopt.getopt(sys.argv[1:], "ha:u:c:n:o:v:V", ["help", "output="])
-    except getopt.GetoptError:
-      usage()
-      sys.exit(2)
-
-    for o, a in opts:
-      o = o.strip()
-      a = a.strip()
-      if o == "-v":
-        verbose = True
-        verbose_filename = a
-      if o == "-V":
-        print "%s\n" % FENCE_RELEASE_NAME
-        print "%s\n" % REDHAT_COPYRIGHT
-        print "%s\n" % BUILD_DATE
-        sys.exit(0)
-      if o in ("-h", "--help"):
-        usage()
-	sys.exit(0)
-      if o == "-a":
-        params['ipaddr'] = a
-      if o == "-u":
-        params['udpport'] = a
-      if o == "-c":
-        params['community'] = a
-      if o == "-n":
-        switch = ''
-	port   = a
-	if ':' in port:
-	   idx = port.find(':')
-	   switch = port[:idx]
-	   port = port[idx+1:]
-	params['switch'] = switch
-	params['port']   = port
-      if o == "-o":
-        params['option'] = a.lower()
-
-  else: #Get opts from stdin 
-    for line in sys.stdin:
-      val = line.strip().split("=")
-      if len(val) == 2:
-         o = val[0].strip().lower()
-	 a = val[1].strip()
-	 if o == 'verbose':
-	    if a.lower() == 'on' or a.lower() == 'true' or a == '1':
-	       verbose = True
-	 else:
-	    params[o] = a 
-	
-    
-  set_logging(verbose, verbose_filename)
-  
-  
-  ### validation ###
-  
-  try:
-	  if params['ipaddr'] == '':
-		  raise Exception, 'missing ipadddr'
-  except:
-	  log("FENCE: Missing ipaddr param for fence_apc_snmp...exiting", True)
-	  sys.exit(1)
-  if 'udpport' not in params:
-	  params['udpport'] = '161'
-  try:
-	  t = int(params['udpport'])
-	  if t >= 65536 or t < 0:
-		  raise Exception, 'invalid udpport'
-  except:
-	  log("FENCE: Invalid udpport for fence_apc_snmp...exiting", True)
-	  sys.exit(1)
-  if 'community' not in params:
-	  params['community'] = 'private'
-  try:
-	  port = params['port']
-	  if len(port) == 0:
-		  raise Exception, 'missing port'
-  except:
-	  log("FENCE: Missing port param for fence_apc_snmp...exiting", True)
-	  sys.exit(1)
-  if 'switch' not in params:
-	  params['switch'] = ''
-  try:
-	  act = params['option'].lower()
-	  if act in ['on', 'off', 'reboot', 'status']:
-		  params['option'] = act
-	  else:
-		  usage()
-		  sys.exit(3)
-  except:
-	  params['option'] = 'reboot'
-	  
-  ### End of validation ###
-
-  if verbose:
-     log('called with ' + str(params))
-  
-  agent = None
-  dummy, sys_id = SNMP(params).get(oid_sysObjectID)
-  if sys_id not in agents_dir:
-     log('Fence device with \'oid_sysObjectID=' + sys_id + '\' is not supported', True)
-     sys.exit(1)
-  agent = agents_dir[sys_id](params)
-  
-  if params['option'] == 'status':
-	  log('Outlet "%s" - %s is %s' % (params['port'], 
-					  str(agent.resolve_outlet()),
-					  agent.status()))
-  elif params['option'] == 'on':
-	  agent.power_on()
-	  if agent.status() != 'on':
-		  raise Exception, 'Error turning outlet on'
-  elif params['option'] == 'off':
-	  agent.power_off()
-	  if agent.status() != 'off':
-		  raise Exception, 'Error turning outlet off'
-  elif params['option'] == 'reboot':
-	  agent.power_off()
-	  if agent.status() != 'off':
-		  raise Exception, 'Error turning outlet off'
-	  agent.power_on()
-	  if agent.status() != 'on':
-		  raise Exception, 'Error turning outlet on'
-  else:
-	  print 'nothing to do'
-	  sys.exit(1)
-	  pass
-  
-
-  
-def execWithCaptureStatus(command, argv, searchPath = 0, root = '/', stdin = 0,
-			  catchfd = 1, closefd = -1):
-	
-    if not os.access (root + command, os.X_OK):
-        raise Exception, command + " cannot be run"
-    
-    (read, write) = os.pipe()
-    
-    childpid = os.fork()
-    if (not childpid):
-        if (root and root != '/'): os.chroot (root)
-        if isinstance(catchfd, tuple):
-            for fd in catchfd:
-                os.dup2(write, fd)
-        else:
-            os.dup2(write, catchfd)
-        os.close(write)
-        os.close(read)
-	
-        if closefd != -1:
-            os.close(closefd)
-	
-        if stdin:
-            os.dup2(stdin, 0)
-            os.close(stdin)
-	
-        if (searchPath):
-            os.execvp(command, argv)
-        else:
-            os.execv(command, argv)
-	
-        sys.exit(1)
-    
-    os.close(write)
-    
-    rc = ""
-    s = "1"
-    while (s):
-        select.select([read], [], [])
-        s = os.read(read, 1000)
-        rc = rc + s
-    
-    os.close(read)
-    
-    try:
-        (pid, status) = os.waitpid(childpid, 0)
-    except OSError, (errno, msg):
-        print __name__, "waitpid:", msg
-    
-    if os.WIFEXITED(status) and (os.WEXITSTATUS(status) == 0):
-        status = os.WEXITSTATUS(status)
-    else:
-        status = -1
-    
-    return (rc, status)
+	device_opt = [ "help", "version", "agent", "quiet", "verbose", "debug",
+		       "action", "ipaddr", "login", "passwd", "passwd_script",
+		       "test", "port", "no_login", "no_password",
+		       "snmp_version", "community", "snmp_auth_prot", "snmp_sec_level",
+		       "snmp_priv_prot", "snmp_priv_passwd", "snmp_priv_passwd_script",
+		       "udpport" ]
+
+	apc_snmp_define_defaults()
+
+	options=check_input(device_opt,process_input(device_opt))
+
+        ## Support for -n [switch]:[plug] notation that was used before
+	if ((options.has_key("-n")) and (-1 != options["-n"].find(":"))):
+		(switch, plug) = options["-n"].split(":", 1)
+		if ((switch.isdigit()) and (plug.isdigit())):
+		        options["-s"] = switch
+			options["-n"] = plug
+
+	if (not (options.has_key("-s"))):
+		options["-s"]="1"
+
+	# Operate the fencing device
+	fence_action(FencingSnmp(options), options, set_power_status, get_power_status)
 
 if __name__ == "__main__":
-	ret = main()
-	sys.exit(ret)
+	main()


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2009-06-26 10:43 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-06-26 10:43 cluster: RHEL5 - Resolves: #498983 - fence_apc_snmp affected by symlink attack vulnerabilities Marek Grác

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).