Skip to content

Commit

Permalink
Enable certificate checking (if available)
Browse files Browse the repository at this point in the history
  • Loading branch information
MStrecke committed Nov 1, 2016
1 parent a3a803a commit 28ae534
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 7 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,20 @@ Please note
* The non HD cameras use a different set of CGI commands and are not covered in this implementation.
* The behaviour of the camera changes slightly with each new firmware version. Please include model and firmware version when sending bug reports (run `camtest.py` from the command line).

Certificate checking
-------------------

Since version 2.7.9 Python is checking certificates used in https connections.

This works fine with most sites on the internet because their certificates are signed by major
certificate authorities and Python has the means to verify their signatures.

However, most cameras use self-signed certificates which will fail this check and throw an exception.

The certificate checking is controlled by the parameter `context.` See `camtest.py` for an example.
This [blog entry](http://tuxpool.blogspot.de/2016/05/accessing-servers-with-self-signed.html) shows how to create a context that fits your camera.

Unfortunately the `context` parameter was first added in Python 3.4.3. Between Python 2.7.9 and 3.4.3
you either have to refrain from using https with self-signed certs or you have to tweak your system
(i.e. install the camera certificate yourself in the system, change the host file, etc) so that the check is
successful without using `context`.
17 changes: 15 additions & 2 deletions camtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from __future__ import print_function

from foscontrol import Cam
import sys

try: # PY3
from configparser import ConfigParser
Expand All @@ -19,15 +20,27 @@
config = ConfigParser()

# see cam.cfg.example
config.readfp(open('cam.cfg'))
config.read(['cam.cfg'])
prot = config.get('general', 'protocol')
host = config.get('general', 'host')
port = config.get('general', 'port')
user = config.get('general', 'user')
passwd = config.get('general', 'password')

if sys.hexversion < 0x03040300:
# parameter context not available
ctx = None
else:
# disable cert checking
# see also http://tuxpool.blogspot.de/2016/05/accessing-servers-with-self-signed.html
import ssl

ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

# connection to the camera
do = Cam(prot, host, port, user, passwd)
do = Cam(prot, host, port, user, passwd, context=ctx)

# display basic camera info
res = do.getDevInfo()
Expand Down
18 changes: 13 additions & 5 deletions foscontrol/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
except:
from urllib.parse import urlencode, unquote

def my_urlopen(url, data=None, context=None):
if sys.hexversion < 0x03040300:
# context not implemented
return urlopen(url, data=data)
else:
return urlopen(url, data=data, context=context)

def encode_multipart(fields, files, boundary=None):
"""
Expand Down Expand Up @@ -371,18 +377,20 @@ class CamBase(object):
- resultObj param "result" is converted to integer, if possible
"""

def __init__(self, prot, host, port, user, password):
def __init__(self, prot, host, port, user, password, context=None):
"""
:param prot: protocol used ("http" or "https")
:param host: hostname (e.g. "www.example.com")
:param port: port used (e.g. 88 or 443)
:param user: username of account in camera
:param password: password of account in camera
:param context; context for secure TLS connections
"""

self.base = "%s://%s:%s/cgi-bin/CGIProxy.fcgi" % (prot, host, port)
self.user = user
self.password = password
self.context = context

self.debugfile = None
self.consoleDump = False
Expand Down Expand Up @@ -491,10 +499,10 @@ def sendcommand(self, cmd, param=None, raw=False, doBool=None, headers=None, dat
url = self.base + "?" + ps

if headers is None:
retdata = urlopen(url, data=data).read()
retdata = my_urlopen(url, data=data, context=self.context).read()
else:
request = Request(url, data=data, headers=headers)
retdata = urlopen(request).read()
retdata = my_urlopen(request, context=self.context).read()

if self.consoleDump:
print("%s\n\n" % retdata)
Expand Down Expand Up @@ -798,7 +806,7 @@ def exportConfig(self):
if w.result == 0:
link = "/configs/export/%s" % w.fileName
link2 = urljoin(self.base, link)
data = urlopen(link2).read()
data = my_urlopen(link2, context=self.context).read()
return (data, w.fileName)
else:
return None
Expand Down Expand Up @@ -1169,7 +1177,7 @@ def snapPicture(self):

link2 = urljoin(self.base, link)

data = urlopen(link2).read()
data = my_urlopen(link2, context=self.context).read()
return (data, fname)

def getPTZSpeed(self):
Expand Down
58 changes: 58 additions & 0 deletions snapshot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-

from __future__ import print_function

from foscontrol import Cam
import sys

try: # PY3
from configparser import ConfigParser
except ImportError:
from ConfigParser import SafeConfigParser as ConfigParser

################################
# Don't forget to edit cam.cfg #
# to reflect you setup! #
################################

if __name__ == "__main__":
config = ConfigParser()

# see cam.cfg.example
config.read(['cam.cfg'])
prot = config.get('general', 'protocol')
host = config.get('general', 'host')
port = config.get('general', 'port')
user = config.get('general', 'user')
passwd = config.get('general', 'password')

if sys.hexversion < 0x03040300:
# parameter context not available
ctx = None
else:
# disable cert checking
# see also http://tuxpool.blogspot.de/2016/05/accessing-servers-with-self-signed.html
import ssl

ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

# connection to the camera
do = Cam(prot, host, port, user, passwd, context=ctx)

(img, fnm) = do.snapPicture()
# Possible errors/exceptions:
#
# urllib.error.URLError (e.g. no route to host)
# ssl.CertificateError (e.g. wrong or no ssl certificate)
# img == None (e.g. wrong password)

if img is not None:
print('Writing picture')
open('/tmp/test.jpg', 'wb').write(img)
else:
print('No picture')


0 comments on commit 28ae534

Please sign in to comment.