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).