From 7f5c896c2a1bfd3bbe9bd3b55dce3084c39dec99 Mon Sep 17 00:00:00 2001 From: CentOS Buildsys Date: Oct 18 2013 14:11:04 +0000 Subject: import python-nss-0.14.0-3.el7.src.rpm --- diff --git a/.python-nss.metadata b/.python-nss.metadata new file mode 100644 index 0000000..12dcef1 --- /dev/null +++ b/.python-nss.metadata @@ -0,0 +1 @@ +dd0d0b434b3489fa52fabe4d7e32a9208c2a3a98 SOURCES/python-nss-0.14.0.tar.bz2 diff --git a/README.md b/README.md deleted file mode 100644 index 0e7897f..0000000 --- a/README.md +++ /dev/null @@ -1,5 +0,0 @@ -The master branch has no content - -Look at the c7 branch if you are working with CentOS-7, or the c4/c5/c6 branch for CentOS-4, 5 or 6 - -If you find this file in a distro specific branch, it means that no content has been checked in yet diff --git a/SOURCES/python-nss-0.14.1.patch b/SOURCES/python-nss-0.14.1.patch new file mode 100644 index 0000000..75ba600 --- /dev/null +++ b/SOURCES/python-nss-0.14.1.patch @@ -0,0 +1,2416 @@ +diff -N -u -r python-nss-0.14.0/doc/ChangeLog python-nss-0.14.1/doc/ChangeLog +--- python-nss-0.14.0/doc/ChangeLog 2013-05-02 17:29:27.000000000 -0400 ++++ python-nss-0.14.1/doc/ChangeLog 2013-10-09 09:35:29.000000000 -0400 +@@ -1,5 +1,37 @@ +-2013-04-24 John Dennis 0.14.0 +- External Changes ++2013-10-09 John Dennis 0.14.1 ++ ++ Modifications only to tests and examples. ++ ++ * Fix bug in ssl_example.py and test_client_server.py where complete ++ data was not read from socket. The Beast CVE fix in NSS causes ++ only one octet to be sent in the first socket packet and then the ++ remaining data is sent normally, this is known as 1/n-1 record ++ splitting. The example and test SSL code sent short messages and ++ then did a sock.recv(1024). We had always received the entire ++ message in one sock.recv() call because it was so short. But ++ sock.recv() does not guarantee how much data will be received, ++ thus this was a coding mistake. The solution is straight forward, ++ use newlines as a record separator and call sock.readline() ++ instead of sock.recv(). sock.readline() calls sock.recv() ++ internally until a complete line is read or the socket is closed. ++ ++ * Rewrite setup_certs.py, it was written like an expect script ++ reacting to prompts read from a pseudo terminal but it was fragile ++ and would hang on some systems. New version uses temporary ++ password file and writes hardcoded responses to the stdin of ++ certuil and modutil. ++ ++ * setup_certs now creates a new sql sytle NSS database (sql:pki) ++ ++ * All tests and examples now load the sql:pki database. Command line ++ arg and variable changed from dbdir to db_name to reflect the ++ database specification is no longer just a directory. ++ ++ * All command line process in test and examples now uses modern ++ argparse module instead of deprecated getopt and optparse. Some ++ command line args were tweaked. ++ ++2013-04-24 John Dennis 0.14.0 External Changes + ---------------- + + The primary enhancements in this version is support of certifcate +diff -N -u -r python-nss-0.14.0/doc/examples/cert_dump.py python-nss-0.14.1/doc/examples/cert_dump.py +--- python-nss-0.14.0/doc/examples/cert_dump.py 2013-04-23 13:36:53.000000000 -0400 ++++ python-nss-0.14.1/doc/examples/cert_dump.py 2013-10-08 12:59:24.000000000 -0400 +@@ -18,10 +18,10 @@ + components of a cert. + ''' + ++import argparse ++import getpass + import os + import sys +-import getopt +-import getpass + + from nss.error import NSPRError + import nss.io as io +@@ -93,57 +93,34 @@ + + # ----------------------------------------------------------------------------- + +-usage_str = ''' +--p --pem read the certifcate in PEM ascii format (default) +--d --der read the certifcate in DER binary format +--P --print-cert print the cert using the internal rendering code +-''' +- +-def usage(): +- print usage_str +- +-try: +- opts, args = getopt.getopt(sys.argv[1:], "hpdP", +- ["help", "pem", "der", "print-cert"]) +-except getopt.GetoptError: +- # print help information and exit: +- usage() +- sys.exit(2) +- +- +-filename = 'cert.der' +-is_pem_format = True +-print_cert = False +- +-for o, a in opts: +- if o in ("-H", "--help"): +- usage() +- sys.exit() +- elif o in ("-p", "--pem"): +- is_pem_format = True +- elif o in ("-d", "--der"): +- is_pem_format = False +- elif o in ("-P", "--print-cert"): +- print_cert = True +- +- +-filename = sys.argv[1] ++parser = argparse.ArgumentParser(description='cert formatting example', ++ formatter_class=argparse.ArgumentDefaultsHelpFormatter) ++parser.add_argument('-f', '--cert-format', choices=['pem', 'der'], ++ help='format of input cert') ++parser.add_argument('-p', '--print-cert', action='store_true', ++ help='print the cert using the internal rendering code') ++parser.add_argument('cert_file', nargs=1, ++ help='input cert file to process') ++ ++parser.set_defaults(cert_format='pem', ++ print_cert=False ++ ) ++options = parser.parse_args() + + # Perform basic configuration and setup + nss.nss_init_nodb() + +-if len(args): +- filename = args[0] ++filename = options.cert_file[0] + + print "certificate filename=%s" % (filename) + + # Read the certificate as DER encoded data +-si = nss.read_der_from_file(filename, is_pem_format) ++si = nss.read_der_from_file(filename, options.cert_format == 'pem') + # Parse the DER encoded data returning a Certificate object + cert = nss.Certificate(si) + + # Useful for comparing the internal cert rendering to what this script generates. +-if print_cert: ++if options.print_cert: + print cert + + # Get the extension list from the certificate +diff -N -u -r python-nss-0.14.0/doc/examples/httplib_example.py python-nss-0.14.1/doc/examples/httplib_example.py +--- python-nss-0.14.0/doc/examples/httplib_example.py 2013-04-15 13:05:46.000000000 -0400 ++++ python-nss-0.14.1/doc/examples/httplib_example.py 2013-10-08 13:04:03.000000000 -0400 +@@ -4,13 +4,13 @@ + # License, v. 2.0. If a copy of the MPL was not distributed with this + # file, You can obtain one at http://mozilla.org/MPL/2.0/. + +-import sys ++import argparse + import errno +-import getopt +-import urlparse +-import httplib + import getpass ++import httplib + import logging ++import sys ++import urlparse + + from nss.error import NSPRError + import nss.io as io +@@ -19,14 +19,6 @@ + + #------------------------------------------------------------------------------ + +-httplib_debug_level = 0 +-logging_debug_level = logging.INFO +-certdir = 'pki' +-password = '' +-nickname = '' +-url = 'https://sourceforge.net/projects/python' +-use_ssl = True +-use_connection_class = True + timeout_secs = 3 + + #------------------------------------------------------------------------------ +@@ -94,28 +86,6 @@ + logging.debug('cert valid %s for "%s"', cert_is_valid, cert.subject) + return cert_is_valid + +-def client_auth_data_callback(ca_names, chosen_nickname, password, certdb): +- cert = None +- if chosen_nickname: +- try: +- cert = nss.find_cert_from_nickname(chosen_nickname, password) +- priv_key = nss.find_key_by_any_cert(cert, password) +- return cert, priv_key +- except NSPRError: +- return False +- else: +- nicknames = nss.get_cert_nicknames(certdb, nss.SEC_CERT_NICKNAMES_USER) +- for nickname in nicknames: +- try: +- cert = nss.find_cert_from_nickname(nickname, password) +- if cert.check_valid_times(): +- if cert.has_signer_in_ca_names(ca_names): +- priv_key = nss.find_key_by_any_cert(cert, password) +- return cert, priv_key +- except NSPRError: +- return False +- return False +- + def password_callback(slot, retry, password): + if not retry and password: return password + return getpass.getpass("Enter password for %s: " % slot.token_name); +@@ -136,7 +106,7 @@ + if not dbdir: + raise RuntimeError("dbdir is required") + +- logging.debug('%s init %s', self.__class__.__name__, host) ++ logging.debug('%s init host=%s dbdir=%s', self.__class__.__name__, host, dbdir) + if not nss.nss_is_initialized(): nss.nss_init(dbdir) + self.sock = None + ssl.set_domestic_policy() +@@ -231,33 +201,46 @@ + #------------------------------------------------------------------------------ + + +-opts, args = getopt.getopt(sys.argv[1:], +- 'Dd:n:w:sScC', +- ['debuglevel','certdir=','nickname=','password=', +- 'use-ssl', 'no-ssl', 'use-connection-class', 'no-connection-class']) +-for o, a in opts: +- if o in('-D', '--httplib_debug_level'): +- httplib_debug_level = httplib_debug_level + 1 +- elif o in ("-d", "--certdir"): +- certdir = a +- elif o in ("-n", "--nickname"): +- nickname = a +- elif o in ("-w", "--password"): +- password = a +- elif o in ("-s", "--use-ssl"): +- use_ssl = True +- elif o in ("-S", "--no-ssl"): +- use_ssl = False +- elif o in ("-c", "--use-connection-class"): +- use_connection_class = True +- elif o in ("-C", "--no-connection-class"): +- use_connection_class = False ++parser = argparse.ArgumentParser(description='httplib example') + +-if len(args) > 0: +- url = args[0] ++parser.add_argument('-d', '--db-name', ++ help='NSS database name (e.g. "sql:pki")') + ++parser.add_argument('--db-passwd', ++ help='NSS database password') + +-if httplib_debug_level > 0: ++parser.add_argument('-s', '--ssl', dest='use_ssl', action='store_true', ++ help='use SSL connection') ++ ++parser.add_argument('-S', '--no-ssl', dest='use_ssl', action='store_false', ++ help='do not use SSL connection') ++ ++parser.add_argument('-c', '--connection-class', dest='use_connection_class', action='store_true', ++ help='use connection class') ++ ++parser.add_argument('-C', '--no-connection-class', dest='use_connection_class', action='store_false', ++ help='do not use connection class') ++ ++parser.add_argument('-D', '--httplib-debug-level', action='count', ++ help='httplib debug level') ++ ++parser.add_argument('url', nargs=1, ++ help='URL to open (e.g. "https://sourceforge.net/projects/python"') ++ ++parser.set_defaults(db_name = 'sql:pki', ++ db_passwd = 'db_passwd', ++ httplib_debug_level = 0, ++ use_ssl = True, ++ use_connection_class = True, ++ ) ++ ++options = parser.parse_args() ++ ++ ++url = options.url[0] ++ ++logging_debug_level = logging.INFO ++if options.httplib_debug_level > 0: + logging_debug_level = logging.DEBUG + else: + logging_debug_level = logging.INFO +@@ -269,7 +252,7 @@ + # Perform basic configuration and setup + + url_components = urlparse.urlsplit(url) +-if use_ssl: ++if options.use_ssl: + url_components.schema = 'https' + else: + url_components.schema = 'http' +@@ -280,14 +263,14 @@ + print "ERROR: bad url \"%s\"" % (url) + sys.exit(1) + +-if use_connection_class: +- if use_ssl: ++if options.use_connection_class: ++ if options.use_ssl: + logging.info("Start (using NSSConnection class) %s", url) +- conn = NSSConnection(url_components.netloc, 443, dbdir="/etc/pki/nssdb") ++ conn = NSSConnection(url_components.netloc, 443, dbdir=options.db_name) + else: + logging.info("Start (using NSPRConnection class) %s", url) + conn = NSPRConnection(url_components.netloc, 80) +- conn.set_debuglevel(httplib_debug_level) ++ conn.set_debuglevel(options.httplib_debug_level) + conn.connect() + conn.request("GET", "/") + response = conn.getresponse() +@@ -302,13 +285,13 @@ + print data + conn.close() + else: +- if use_ssl: ++ if options.use_ssl: + logging.info("Start (using NSSHTTPS class) %s", url) +- h = NSSHTTPS(url_components.netloc, 443, dbdir="/etc/pki/nssdb") ++ h = NSSHTTPS(url_components.netloc, 443, dbdir=options.db_name) + else: + logging.info("Start (using NSPRHTTP class) %s", url) + h = NSPRHTTP(url_components.netloc, 80) +- h.set_debuglevel(httplib_debug_level) ++ h.set_debuglevel(options.httplib_debug_level) + h.connect() + h.putrequest('GET', '/') + h.endheaders() +diff -N -u -r python-nss-0.14.0/doc/examples/ssl_example.py python-nss-0.14.1/doc/examples/ssl_example.py +--- python-nss-0.14.0/doc/examples/ssl_example.py 2013-04-15 13:05:46.000000000 -0400 ++++ python-nss-0.14.1/doc/examples/ssl_example.py 2013-10-09 00:07:54.000000000 -0400 +@@ -7,10 +7,10 @@ + import warnings + warnings.simplefilter( "always", DeprecationWarning) + ++import argparse ++import getpass + import os + import sys +-import getopt +-import getpass + + from nss.error import NSPRError + import nss.io as io +@@ -24,19 +24,7 @@ + REQUEST_CLIENT_CERT_ALWAYS = 3 + REQUIRE_CLIENT_CERT_ALWAYS = 4 + +-# command line parameters, default them to something reasonable +-client = False +-server = False +-password = 'db_passwd' +-use_ssl = True +-client_cert_action = NO_CLIENT_CERT +-certdir = 'pki' +-hostname = os.uname()[1] +-server_nickname = 'test_server' +-client_nickname = 'test_user' +-port = 1234 + timeout_secs = 3 +-family = io.PR_AF_UNSPEC + + # ----------------------------------------------------------------------------- + # Utility Functions +@@ -63,7 +51,7 @@ + if pin_args is None: + pin_args = () + +- print "cert:\n%s" % cert ++ print "peer cert:\n%s" % cert + + # Define how the cert is being used based upon the is_server flag. This may + # seem backwards, but isn't. If we're a server we're trying to validate a +@@ -149,30 +137,30 @@ + valid_addr = False + # Get the IP Address of our server + try: +- addr_info = io.AddrInfo(hostname) ++ addr_info = io.AddrInfo(options.hostname) + except Exception, e: +- print "could not resolve host address \"%s\"" % hostname ++ print "could not resolve host address \"%s\"" % options.hostname + return + + for net_addr in addr_info: +- if family != io.PR_AF_UNSPEC: +- if net_addr.family != family: continue +- net_addr.port = port ++ if options.family != io.PR_AF_UNSPEC: ++ if net_addr.family != options.family: continue ++ net_addr.port = options.port + +- if use_ssl: ++ if options.use_ssl: + sock = ssl.SSLSocket(net_addr.family) + + # Set client SSL socket options + sock.set_ssl_option(ssl.SSL_SECURITY, True) + sock.set_ssl_option(ssl.SSL_HANDSHAKE_AS_CLIENT, True) +- sock.set_hostname(hostname) ++ sock.set_hostname(options.hostname) + + # Provide a callback which notifies us when the SSL handshake is complete + sock.set_handshake_callback(handshake_callback) + + # Provide a callback to supply our client certificate info +- sock.set_client_auth_data_callback(client_auth_data_callback, client_nickname, +- password, nss.get_default_certdb()) ++ sock.set_client_auth_data_callback(client_auth_data_callback, options.client_nickname, ++ options.password, nss.get_default_certdb()) + + # Provide a callback to verify the servers certificate + sock.set_auth_certificate_callback(auth_certificate_callback, +@@ -192,17 +180,18 @@ + + if not valid_addr: + print "Could not establish valid address for \"%s\" in family %s" % \ +- (hostname, io.addr_family_name(family)) ++ (options.hostname, io.addr_family_name(options.family)) + return + + # Talk to the server + try: +- sock.send("Hello") +- buf = sock.recv(1024) ++ sock.send('Hello' + '\n') # newline is protocol record separator ++ buf = sock.readline() + if not buf: + print "client lost connection" + sock.close() + return ++ buf = buf.rstrip() # remove newline record separator + print "client received: %s" % (buf) + except Exception, e: + print e.strerror +@@ -221,7 +210,7 @@ + + try: + sock.close() +- if use_ssl: ++ if options.use_ssl: + ssl.clear_session_cache() + except Exception, e: + print e +@@ -231,36 +220,34 @@ + # ----------------------------------------------------------------------------- + + def Server(): +- global family +- +- # Perform basic SSL server configuration +- ssl.set_default_cipher_pref(ssl.SSL_RSA_WITH_NULL_MD5, True) +- ssl.config_server_session_id_cache() +- +- # Get our certificate and private key +- server_cert = nss.find_cert_from_nickname(server_nickname, password) +- priv_key = nss.find_key_by_any_cert(server_cert, password) +- server_cert_kea = server_cert.find_kea_type(); +- +- print "server cert:\n%s" % server_cert +- + # Setup an IP Address to listen on any of our interfaces +- if family == io.PR_AF_UNSPEC: +- family = io.PR_AF_INET +- net_addr = io.NetworkAddress(io.PR_IpAddrAny, port, family) ++ if options.family == io.PR_AF_UNSPEC: ++ options.family = io.PR_AF_INET ++ net_addr = io.NetworkAddress(io.PR_IpAddrAny, options.port, options.family) ++ ++ if options.use_ssl: ++ # Perform basic SSL server configuration ++ ssl.set_default_cipher_pref(ssl.SSL_RSA_WITH_NULL_MD5, True) ++ ssl.config_server_session_id_cache() ++ ++ # Get our certificate and private key ++ server_cert = nss.find_cert_from_nickname(options.server_nickname, options.password) ++ priv_key = nss.find_key_by_any_cert(server_cert, options.password) ++ server_cert_kea = server_cert.find_kea_type(); ++ ++ print "server cert:\n%s" % server_cert + +- if use_ssl: + sock = ssl.SSLSocket(net_addr.family) + + # Set server SSL socket options +- sock.set_pkcs11_pin_arg(password) ++ sock.set_pkcs11_pin_arg(options.password) + sock.set_ssl_option(ssl.SSL_SECURITY, True) + sock.set_ssl_option(ssl.SSL_HANDSHAKE_AS_SERVER, True) + + # If we're doing client authentication then set it up +- if client_cert_action >= REQUEST_CLIENT_CERT_ONCE: ++ if options.client_cert_action >= REQUEST_CLIENT_CERT_ONCE: + sock.set_ssl_option(ssl.SSL_REQUEST_CERTIFICATE, True) +- if client_cert_action == REQUIRE_CLIENT_CERT_ONCE: ++ if options.client_cert_action == REQUIRE_CLIENT_CERT_ONCE: + sock.set_ssl_option(ssl.SSL_REQUIRE_CERTIFICATE, True) + sock.set_auth_certificate_callback(auth_certificate_callback, nss.get_default_certdb()) + +@@ -278,7 +265,7 @@ + while True: + # Accept a connection from a client + client_sock, client_addr = sock.accept() +- if use_ssl: ++ if options.use_ssl: + client_sock.set_handshake_callback(handshake_callback) + + print "client connect from: %s" % (client_addr) +@@ -286,14 +273,15 @@ + while True: + try: + # Handle the client connection +- buf = client_sock.recv(1024) ++ buf = client_sock.readline() + if not buf: + print "server lost lost connection to %s" % (client_addr) + break + ++ buf = buf.rstrip() # remove newline record separator + print "server received: %s" % (buf) + +- client_sock.send("Goodbye") ++ client_sock.send('Goodbye' + '\n') # newline is protocol record separator + try: + client_sock.shutdown(io.PR_SHUTDOWN_RCV) + client_sock.close() +@@ -308,7 +296,7 @@ + try: + sock.shutdown() + sock.close() +- if use_ssl: ++ if options.use_ssl: + ssl.shutdown_server_session_id_cache() + except Exception, e: + print e +@@ -316,141 +304,121 @@ + + # ----------------------------------------------------------------------------- + +-usage_str = ''' +--C --client run as the client (default: %(client)s) +--S --server run as the server (default: %(server)s) +--d --certdir certificate directory (default: %(certdir)s) +--h --hostname host to connect to (default: %(hostname)s) +--f --family may be inet|inet6|unspec (default: %(family)s) +- if unspec client tries all addresses returned by AddrInfo +- server binds to IPv4 "any" wildcard address +- if inet client tries IPv4 addresses returned by AddrInfo +- server binds to IPv4 "any" wildcard address +- if inet6 client tries IPv6 addresses returned by AddrInfo +- server binds to IPv6 "any" wildcard address +--4 --inet set family to inet (see family) +--6 --inet6 set family to inet6 (see family) +--n --server_nickname server certificate nickname (default: %(server_nickname)s) +--N --client_nickname client certificate nickname (default: %(client_nickname)s) +--w --password certificate database password (default: %(password)s) +--p --port host port (default: %(port)s) +--e --encrypt use SSL (default) (default: %(encrypt)s) +--E --noencrypt don't use SSL (default: %(noencrypt)s) +--f --require_cert_once (default: %(require_cert_once)s) +--F --require_cert_always (default: %(require_cert_always)s) +--r --request_cert_once (default: %(request_cert_once)s) +--R --request_cert_always (default: %(request_cert_always)s) +--H --help +-''' % { +- 'client' : client, +- 'server' : server, +- 'certdir' : certdir, +- 'hostname' : hostname, +- 'family' : io.addr_family_name(family), +- 'server_nickname' : server_nickname, +- 'client_nickname' : client_nickname, +- 'password' : password, +- 'port' : port, +- 'encrypt' : use_ssl is True, +- 'noencrypt' : use_ssl is False, +- 'require_cert_once' : client_cert_action == REQUIRE_CLIENT_CERT_ONCE, +- 'require_cert_always' : client_cert_action == REQUIRE_CLIENT_CERT_ALWAYS, +- 'request_cert_once' : client_cert_action == REQUEST_CLIENT_CERT_ONCE, +- 'request_cert_always' : client_cert_action == REQUEST_CLIENT_CERT_ALWAYS, +- } +- +-def usage(): +- print usage_str +- +-try: +- opts, args = getopt.getopt(sys.argv[1:], "Hd:h:f:46n:N:w:p:CSeE", +- ["help", "certdir=", "hostname=", +- "family", "inet", "inet6", +- "server_nickname=", "client_nickname=", +- "password=", "port=", +- "client", "server", "encrypt", "noencrypt", +- "require_cert_once", "require_cert_always", +- "request_cert_once", "request_cert_always", +- ]) +-except getopt.GetoptError: +- # print help information and exit: +- usage() +- sys.exit(2) +- +- +-for o, a in opts: +- if o in ("-d", "--certdir"): +- certdir = a +- elif o in ("-h", "--hostname"): +- hostname = a +- elif o in ("-f", "--family"): +- if a == "inet": ++class FamilyArgAction(argparse.Action): ++ def __call__(self, parser, namespace, values, option_string=None): ++ value = values[0] ++ if value == "inet": + family = io.PR_AF_INET +- elif a == "inet6": ++ elif value == "inet6": + family = io.PR_AF_INET6 +- elif a == "unspec": ++ elif value == "unspec": + family = io.PR_AF_UNSPEC + else: +- print "unknown address family (%s)" % (a) +- usage() +- sys.exit() +- elif o in ("-4", "--inet"): +- family = io.PR_AF_INET +- elif o in ("-6", "--inet6"): +- family = io.PR_AF_INET6 +- elif o in ("-n", "--server_nickname"): +- server_nickname = a +- elif o in ("-N", "--client_nickname"): +- client_nickname = a +- elif o in ("-w", "--password"): +- password = a +- elif o in ("-p", "--port"): +- port = int(a) +- elif o in ("-C", "--client"): +- client = True +- elif o in ("-S", "--server"): +- server = True +- elif o in ("-e", "--encrypt"): +- use_ssl = True +- elif o in ("-E", "--noencrypt"): +- use_ssl = False +- elif o in ("--require_cert_once"): +- client_cert_action = REQUIRE_CLIENT_CERT_ONCE +- elif o in ("--require_cert_always"): +- client_cert_action = REQUIRE_CLIENT_CERT_ALWAYS +- elif o in ("--request_cert_once"): +- client_cert_action = REQUEST_CLIENT_CERT_ONCE +- elif o in ("--request_cert_always"): +- client_cert_action = REQUEST_CLIENT_CERT_ALWAYS +- elif o in ("-H", "--help"): +- usage() +- sys.exit() +- else: +- usage() +- sys.exit() ++ raise argparse.ArgumentError(self, "unknown address family (%s)" % (value)) ++ setattr(namespace, self.dest, family) ++ ++parser = argparse.ArgumentParser(description='SSL example') ++ ++parser.add_argument('-C', '--client', action='store_true', ++ help='run as the client') ++ ++parser.add_argument('-S', '--server', action='store_true', ++ help='run as the server') ++ ++parser.add_argument('-d', '--db-name', ++ help='NSS database name (e.g. "sql:pki")') ++ ++parser.add_argument('-H', '--hostname', ++ help='host to connect to') ++ ++parser.add_argument('-f', '--family', ++ choices=['unspec', 'inet', 'inet6'], ++ dest='family', action=FamilyArgAction, nargs=1, ++ help=''' ++ If unspec client tries all addresses returned by AddrInfo, ++ server binds to IPv4 "any" wildcard address. ++ ++ If inet client tries IPv4 addresses returned by AddrInfo, ++ server binds to IPv4 "any" wildcard address. ++ ++ If inet6 client tries IPv6 addresses returned by AddrInfo, ++ server binds to IPv6 "any" wildcard address''') + +-if client and server: ++parser.add_argument('-4', '--inet', ++ dest='family', action='store_const', const=io.PR_AF_INET, ++ help='set family to inet (see family)') ++ ++parser.add_argument('-6', '--inet6', ++ dest='family', action='store_const', const=io.PR_AF_INET6, ++ help='set family to inet6 (see family)') ++ ++parser.add_argument('-n', '--server-nickname', ++ help='server certificate nickname') ++ ++parser.add_argument('-N', '--client-nickname', ++ help='client certificate nickname') ++ ++parser.add_argument('-w', '--password', ++ help='certificate database password') ++ ++parser.add_argument('-p', '--port', type=int, ++ help='host port') ++ ++parser.add_argument('-e', '--encrypt', dest='use_ssl', action='store_true', ++ help='use SSL connection') ++ ++parser.add_argument('-E', '--no-encrypt', dest='use_ssl', action='store_false', ++ help='do not use SSL connection') ++ ++parser.add_argument('--require-cert-once', dest='client_cert_action', ++ action='store_const', const=REQUIRE_CLIENT_CERT_ONCE) ++ ++parser.add_argument('--require-cert-always', dest='client_cert_action', ++ action='store_const', const=REQUIRE_CLIENT_CERT_ALWAYS) ++ ++parser.add_argument('--request-cert-once', dest='client_cert_action', ++ action='store_const', const=REQUEST_CLIENT_CERT_ONCE) ++ ++parser.add_argument('--request-cert-always', dest='client_cert_action', ++ action='store_const', const=REQUEST_CLIENT_CERT_ALWAYS) ++ ++parser.set_defaults(client = False, ++ server = False, ++ db_name = 'sql:pki', ++ hostname = os.uname()[1], ++ family = io.PR_AF_UNSPEC, ++ server_nickname = 'test_server', ++ client_nickname = 'test_user', ++ password = 'db_passwd', ++ port = 1234, ++ use_ssl = True, ++ client_cert_action = NO_CLIENT_CERT, ++ ) ++ ++options = parser.parse_args() ++ ++if options.client and options.server: + print "can't be both client and server" + sys.exit(1) +-if not (client or server): ++if not (options.client or options.server): + print "must be one of client or server" + sys.exit(1) + + # Perform basic configuration and setup +-if certdir is None: +- nss.nss_init_nodb() ++if options.use_ssl: ++ nss.nss_init(options.db_name) + else: +- nss.nss_init(certdir) ++ nss.nss_init_nodb() + + ssl.set_domestic_policy() + nss.set_password_callback(password_callback) + + # Run as a client or as a server +-if client: ++if options.client: + print "starting as client" + Client() + +-if server: ++if options.server: + print "starting as server" + Server() + +@@ -458,4 +426,3 @@ + nss.nss_shutdown() + except Exception, e: + print e +- +diff -N -u -r python-nss-0.14.0/doc/examples/verify_cert.py python-nss-0.14.1/doc/examples/verify_cert.py +--- python-nss-0.14.0/doc/examples/verify_cert.py 2013-04-15 13:05:46.000000000 -0400 ++++ python-nss-0.14.1/doc/examples/verify_cert.py 2013-10-08 13:04:51.000000000 -0400 +@@ -1,7 +1,7 @@ + #!/usr/bin/python + ++import argparse + import sys +-import optparse + + import nss.nss as nss + import nss.error as nss_error +@@ -75,75 +75,74 @@ + lines.extend(nss.make_line_fmt_tuples(level, msg)) + lines.extend(obj.format_lines(level+1)) + return nss.indented_format(lines) +- ++ + + #------------------------------------------------------------------------------- + + def main(): +- # Command line argument processing +- parser = optparse.OptionParser() +- +- parser.set_defaults(dbdir = '/etc/pki/nssdb', +- db_passwd = 'db_passwd', +- input_format = 'pem', +- check_sig = True, +- print_cert = False, +- with_log = True, +- check_ca = True, +- ) ++ global options + +- param_group = optparse.OptionGroup(parser, 'NSS Database', +- 'Specify & control the NSS Database') ++ parser = argparse.ArgumentParser(description='certificate validation example') + +- param_group.add_option('-d', '--dbdir', dest='dbdir', +- help='NSS database directory, default="%default"') +- param_group.add_option('-P', '--db-passwd', dest='db_passwd', +- help='NSS database password, default="%default"') ++ # === NSS Database Group === ++ group = parser.add_argument_group('NSS Database', ++ 'Specify & control the NSS Database') ++ group.add_argument('-d', '--db-name', ++ help='NSS database name (e.g. "sql:pki")') + +- parser.add_option_group(param_group) ++ group.add_argument('-P', '--db-passwd', ++ help='NSS database password') + +- param_group = optparse.OptionGroup(parser, 'Certificate', +- 'Specify how the certificate is loaded') ++ # === Certificate Group === ++ group = parser.add_argument_group('Certificate', ++ 'Specify how the certificate is loaded') + +- param_group.add_option('-f', '--file', dest='cert_filename', +- help='read cert from file') +- param_group.add_option('--format', dest='input_format', choices=['pem', 'der'], +- help='import format for certificate (der|pem) default="%default"') +- param_group.add_option('-n', '--nickname', dest='cert_nickname', +- help='load cert from NSS database by looking it up under this nickname') ++ group.add_argument('-f', '--file', dest='cert_filename', ++ help='read cert from file') + ++ group.add_argument('-F', '--input-format', choices=['pem', 'der'], ++ help='format of input cert') + +- parser.add_option_group(param_group) ++ group.add_argument('-n', '--nickname', dest='cert_nickname', ++ help='load cert from NSS database by looking it up under this nickname') + +- param_group = optparse.OptionGroup(parser, 'Validation', +- 'Control the validation') ++ # === Validation Group === ++ group = parser.add_argument_group('Validation', ++ 'Control the validation') + +- param_group.add_option('-u', '--usage', dest='cert_usage', action='append', choices=cert_usage_map.keys(), +- help='may be specified multiple times, default="CheckAllUsages", may be one of: %s' % ', '.join(sorted(cert_usage_map.keys()))) +- param_group.add_option('-c', '--check-sig', action='store_true', dest='check_sig', +- help='check signature default=%default') +- param_group.add_option('-C', '--no-check-sig', action='store_false', dest='check_sig', ++ group.add_argument('-u', '--usage', dest='cert_usage', action='append', choices=cert_usage_map.keys(), ++ help='certificate usage flags, may be specified multiple times') ++ group.add_argument('-c', '--check-sig', action='store_true', dest='check_sig', + help='check signature') +- param_group.add_option('-l', '--log', action='store_true', dest='with_log', +- help='use verify log, default=%default') +- param_group.add_option('-L', '--no-log', action='store_false', dest='with_log', +- help='use verify log, default=%default') +- param_group.add_option('-a', '--check-ca', action='store_true', dest='check_ca', +- help='check if cert is CA, default=%default') +- param_group.add_option('-A', '--no-check-ca', action='store_false', dest='check_ca', +- help='check if cert is CA, default=%default') +- +- parser.add_option_group(param_group) +- +- param_group = optparse.OptionGroup(parser, 'Miscellaneous', +- 'Miscellaneous options') ++ group.add_argument('-C', '--no-check-sig', action='store_false', dest='check_sig', ++ help='do not check signature') ++ group.add_argument('-l', '--log', action='store_true', dest='with_log', ++ help='use verify log') ++ group.add_argument('-L', '--no-log', action='store_false', dest='with_log', ++ help='do not use verify log') ++ group.add_argument('-a', '--check-ca', action='store_true', dest='check_ca', ++ help='check if cert is CA') ++ group.add_argument('-A', '--no-check-ca', action='store_false', dest='check_ca', ++ help='do not check if cert is CA') ++ ++ # === Miscellaneous Group === ++ group = parser.add_argument_group('Miscellaneous', ++ 'Miscellaneous options') + +- param_group.add_option('-p', '--print-cert', action='store_true', dest='print_cert', +- help='print the certificate in a friendly fashion, default=%default') ++ group.add_argument('-p', '--print-cert', action='store_true', dest='print_cert', ++ help='print the certificate in a friendly fashion') + +- parser.add_option_group(param_group) + +- options, args = parser.parse_args() ++ parser.set_defaults(db_name = 'sql:pki', ++ db_passwd = 'db_passwd', ++ input_format = 'pem', ++ check_sig = True, ++ with_log = True, ++ check_ca = True, ++ print_cert = False, ++ ) ++ ++ options = parser.parse_args() + + # Process the command line arguments + +@@ -175,9 +174,9 @@ + return 1 + + # Initialize NSS. +- print indented_output('NSS Database', options.dbdir) ++ print indented_output('NSS Database', options.db_name) + print +- nss.nss_init(options.dbdir) ++ nss.nss_init(options.db_name) + certdb = nss.get_default_certdb() + nss.set_password_callback(password_callback) + +@@ -194,7 +193,7 @@ + except Exception, e: + print e + print >>sys.stderr, 'Unable to load cert nickname "%s" from database "%s"' % \ +- (options.cert_nickname, options.dbdir) ++ (options.cert_nickname, options.db_name) + return 1 + + # Dump the cert if the user wants to see it +@@ -282,5 +281,3 @@ + #------------------------------------------------------------------------------- + if __name__ == "__main__": + sys.exit(main()) +- +- +diff -N -u -r python-nss-0.14.0/doc/examples/verify_server.py python-nss-0.14.1/doc/examples/verify_server.py +--- python-nss-0.14.0/doc/examples/verify_server.py 2013-04-15 13:05:46.000000000 -0400 ++++ python-nss-0.14.1/doc/examples/verify_server.py 2013-10-08 13:00:37.000000000 -0400 +@@ -4,10 +4,10 @@ + # License, v. 2.0. If a copy of the MPL was not distributed with this + # file, You can obtain one at http://mozilla.org/MPL/2.0/. + ++import argparse ++import getpass + import os + import sys +-import getopt +-import getpass + + from nss.error import NSPRError + import nss.io as io +@@ -16,11 +16,6 @@ + + # ----------------------------------------------------------------------------- + +-# command line parameters, default them to something reasonable +-#certdir = '/etc/httpd/alias' +-certdir = '/etc/pki/nssdb' +-hostname = 'www.verisign.com' +-port = 443 + timeout_secs = 3 + + request = '''\ +@@ -104,18 +99,18 @@ + valid_addr = False + # Get the IP Address of our server + try: +- addr_info = io.AddrInfo(hostname) ++ addr_info = io.AddrInfo(options.hostname) + except: +- print "ERROR: could not resolve hostname \"%s\"" % hostname ++ print "ERROR: could not resolve hostname \"%s\"" % options.hostname + return + + for net_addr in addr_info: +- net_addr.port = port ++ net_addr.port = options.port + sock = ssl.SSLSocket(net_addr.family) + # Set client SSL socket options + sock.set_ssl_option(ssl.SSL_SECURITY, True) + sock.set_ssl_option(ssl.SSL_HANDSHAKE_AS_CLIENT, True) +- sock.set_hostname(hostname) ++ sock.set_hostname(options.hostname) + + # Provide a callback which notifies us when the SSL handshake is + # complete +@@ -135,7 +130,7 @@ + continue + + if not valid_addr: +- print "ERROR: could not connect to \"%s\"" % hostname ++ print "ERROR: could not connect to \"%s\"" % options.hostname + return + + try: +@@ -158,48 +153,31 @@ + + # ----------------------------------------------------------------------------- + +-usage_str = ''' +--d --certdir certificate directory (default: %(certdir)s) +--h --hostname host to connect to (default: %(hostname)s) +--p --port host port (default: %(port)s) +-''' % { +- 'certdir' : certdir, +- 'hostname' : hostname, +- 'port' : port, +- } ++parser = argparse.ArgumentParser(description='certificate verification example', ++ formatter_class=argparse.ArgumentDefaultsHelpFormatter) + +-def usage(): +- print usage_str ++parser.add_argument('-d', '--db-name', ++ help='NSS database name (e.g. "sql:pki")') + +-try: +- opts, args = getopt.getopt(sys.argv[1:], "Hd:h:p:", +- ["help", "certdir=", "hostname=", +- "port=", +- ]) +-except getopt.GetoptError: +- # print help information and exit: +- usage() +- sys.exit(2) +- +- +-for o, a in opts: +- if o in ("-d", "--certdir"): +- certdir = a +- if o in ("-h", "--hostname"): +- hostname = a +- if o in ("-p", "--port"): +- port = int(a) +- if o in ("-H", "--help"): +- usage() +- sys.exit() ++parser.add_argument('-H', '--hostname', ++ help='host to connect to') ++ ++parser.add_argument('-p', '--port', type=int, ++ help='host port') ++ ++parser.set_defaults(db_name = 'sql:pki', ++ hostname = 'www.verisign.com', ++ port = 443, ++ ) ++ ++options = parser.parse_args() + + # Perform basic configuration and setup + try: +- nss.nss_init(certdir) ++ nss.nss_init(options.db_name) + ssl.set_domestic_policy() + except Exception, e: + print >>sys.stderr, e.strerror + sys.exit(1) + + client() +- +diff -N -u -r python-nss-0.14.0/src/__init__.py python-nss-0.14.1/src/__init__.py +--- python-nss-0.14.0/src/__init__.py 2013-04-24 16:30:26.000000000 -0400 ++++ python-nss-0.14.1/src/__init__.py 2013-10-08 14:38:28.000000000 -0400 +@@ -163,8 +163,8 @@ + + - Initialize NSS and indicate the certficate database (CertDB):: + +- certdir = './pki' +- ssl.nssinit(certdir) ++ db_name = 'sql:pki' ++ ssl.nssinit(db_name) + + - If you are implementing an SSL server call config_secure_server() + (see ssl_example.py):: +@@ -244,7 +244,7 @@ + future we can find a solution but the immediate goal of the NSS + Python binding was to expose NSS through Python, not necessarily + to solve the larger integration issue of Python run-time and NSPR +- run-time. ++ run-time. + + - NSPR would like to hide the underlying platform socket (in the + NSPR code this is called "osfd"). There are NSPR API's which +@@ -312,5 +312,4 @@ + To be added + + """ +-__version__ = '0.14.0' +- ++__version__ = '0.14.1' +diff -N -u -r python-nss-0.14.0/test/run_tests python-nss-0.14.1/test/run_tests +--- python-nss-0.14.0/test/run_tests 2013-04-30 11:03:54.000000000 -0400 ++++ python-nss-0.14.1/test/run_tests 2013-10-08 12:57:56.000000000 -0400 +@@ -1,21 +1,13 @@ + #!/usr/bin/python + +-import getopt +-import sys ++import argparse + import os ++import sys + import unittest + from util import get_build_dir + + #------------------------------------------------------------------------------- + +-prog_name = os.path.basename(sys.argv[0]) +- +-config = { +- 'in_tree' : True, +-} +- +-#------------------------------------------------------------------------------- +- + def run_tests(): + + import setup_certs +@@ -23,9 +15,11 @@ + import test_cipher + import test_digest + import test_pkcs12 ++ import test_misc ++ import test_ocsp + import test_client_server + +- setup_certs.setup_certs() ++ setup_certs.setup_certs([]) + + loader = unittest.TestLoader() + runner = unittest.TextTestRunner() +@@ -43,62 +37,19 @@ + + #------------------------------------------------------------------------------- + +-class Usage(Exception): +- def __init__(self, msg): +- self.msg = msg +- +-def usage(): +- 'Print command help.' +- +- return '''\ +-%(prog_name)s [-i] +- +--h --help print help +--i --installed runs the test using installed libraries +- instead of "in tree" libraries +- +-Runs unit tests. +-By default test is done "in tree". +- +-Examples: +- +-Run test using libraries built in this tree +-%(prog_name)s +- +-Run post install test +-%(prog_name)s -i +-''' % {'prog_name' : prog_name, +- } +- +-#------------------------------------------------------------------------------- ++def main(): ++ parser = argparse.ArgumentParser(description='run the units (installed or in tree)') ++ parser.add_argument('-i', '--installed', action='store_false', dest='in_tree', ++ help='run tests using installed library') ++ parser.add_argument('-t', '--in-tree', action='store_true', dest='in_tree', ++ help='run tests using devel tree') + +-def main(argv=None): +- if argv is None: +- argv = sys.argv +- +- try: +- try: +- opts, args = getopt.getopt(argv[1:], 'hi', +- ['help', 'installed',]) +- except getopt.GetoptError, e: +- raise Usage(e) +- return 2 +- +- for o, a in opts: +- if o in ('-h', '--help'): +- print >>sys.stdout, usage() +- return 0 +- elif o in ('-i', '--installed'): +- config['in_tree'] = False +- else: +- raise Usage("command argument '%s' not handled, internal error" % o) +- except Usage, e: +- print >>sys.stderr, e.msg +- print >>sys.stderr, "for help use --help" +- return 2 ++ parser.set_defaults(in_tree = False, ++ ) + ++ options = parser.parse_args() + +- if config['in_tree']: ++ if options.in_tree: + # Run the tests "in the tree" + # Rather than testing with installed versions run the test + # with the package built in this tree. +diff -N -u -r python-nss-0.14.0/test/setup_certs.py python-nss-0.14.1/test/setup_certs.py +--- python-nss-0.14.0/test/setup_certs.py 2013-04-18 12:28:05.000000000 -0400 ++++ python-nss-0.14.1/test/setup_certs.py 2013-10-17 11:07:09.000000000 -0400 +@@ -1,345 +1,506 @@ + #!/usr/bin/python + +-import traceback +-import getopt +-import sys +-import os +-import errno ++import argparse ++import atexit + import logging +-import subprocess ++import os + import shutil +-import shlex +-import pty +-import tty +-import re +-import time +- +-#------------------------------------------------------------------------------- +- +-__all__ = ["config", "setup_certs"] +- +-if __name__ == '__main__': +- prog_name = os.path.basename(sys.argv[0]) +-else: +- prog_name = 'setup_certs' +- +-serial_number = 0 +-hostname = os.uname()[1] +-client_username = 'test_user' +- +-config = { +- 'verbose' : False, +- 'debug' : False, +- 'logfile' : 'setup_certs.log', +- 'log_level' : logging.WARN, +- 'interactive' : sys.stdout.isatty(), +- 'dbdir' : os.path.join(os.path.dirname(sys.argv[0]), 'pki'), +- 'db_passwd' : 'db_passwd', +- 'noise_file' : 'noise_file', +- 'ca_subject' : 'CN=Test CA', +- 'ca_nickname' : 'test_ca', +- 'server_subject' : 'CN=%s' % hostname, +- 'server_nickname' : 'test_server', +- 'client_subject' : 'CN=%s' % client_username, +- 'client_nickname' : client_username, +-} ++import subprocess ++import sys ++from string import Template ++import tempfile + + #------------------------------------------------------------------------------- + + class CmdError(Exception): +- def __init__(self, cmd, exit_code, msg): +- self.cmd = cmd +- self.exit_code = exit_code +- self.msg = msg ++ def __init__(self, cmd_args, returncode, message=None, stdout=None, stderr=None): ++ self.cmd_args = cmd_args ++ self.returncode = returncode ++ if message is None: ++ self.message = 'Failed error=%s, ' % (returncode) ++ if stderr: ++ self.message += '"%s", ' % stderr ++ self.message += 'args=%s' % (cmd_args) ++ else: ++ self.message = message ++ self.stdout = stdout ++ self.stderr = stderr + + def __str__(self): +- return "Command \"%s\"\nFailed with exit code = %s\nOutput was:\n%s\n" % \ +- (self.cmd, self.exit_code, self.msg) ++ return self.message + +-#------------------------------------------------------------------------------- + +-def next_serial(): +- global serial_number +- serial_number += 1 +- return serial_number ++def run_cmd(cmd_args, input=None): ++ logging.debug(' '.join(cmd_args)) ++ try: ++ p = subprocess.Popen(cmd_args, ++ stdin=subprocess.PIPE, ++ stdout=subprocess.PIPE, ++ stderr=subprocess.PIPE) ++ stdout, stderr = p.communicate(input) ++ returncode = p.returncode ++ if returncode != 0: ++ raise CmdError(cmd_args, returncode, ++ 'failed %s' % (', '.join(cmd_args)), ++ stdout, stderr) ++ return stdout, stderr ++ except OSError as e: ++ raise CmdError(cmd_args, e.errno, stderr=str(e)) ++ ++def exit_handler(options): ++ logging.debug('in exit handler') ++ ++ if options.passwd_filename is not None: ++ logging.debug('removing passwd_filename=%s', options.passwd_filename) ++ os.remove(options.passwd_filename) ++ ++ if options.noise_filename is not None: ++ logging.debug('removing noise_filename=%s', options.noise_filename) ++ os.remove(options.noise_filename) ++ ++def write_serial(options, serial_number): ++ with open(options.serial_file, 'w') as f: ++ f.write('%x\n' % serial_number) ++ ++ ++def read_serial(options): ++ if not os.path.exists(options.serial_file): ++ write_serial(options, options.serial_number) + +-def create_noise_file(): +- """ +- Generate a noise file to be used when creating a key +- """ +- if os.path.exists(config['noise_file']): +- os.remove(config['noise_file']) +- +- f = open(config['noise_file'], "w") +- f.write(os.urandom(40)) +- f.close() ++ with open(options.serial_file) as f: ++ serial_number = int(f.readline(), 16) ++ return serial_number + +- return + +-def run_cmd(cmd, input=None): +- logging.debug("running command: %s", cmd) ++def init_noise_file(options): ++ '''Generate a noise file to be used when creating a key + +- if input is None: +- stdin = None +- else: +- stdin = subprocess.PIPE ++ We create a temporary file on first use and continue to use ++ the same temporary file for the duration of this process. ++ Each time this function is called it writes new random data ++ into the file. ++ ''' ++ random_data = os.urandom(40) + +- p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) +- stdout, stderr = p.communicate(input) +- status = p.returncode +- if config['verbose']: +- logging.debug("cmd status = %s", status) +- logging.debug("cmd stdout = %s", stdout) +- logging.debug("cmd stderr = %s", stderr) +- return status, stdout, stderr +- +-def run_cmd_with_prompts(cmd, prompts): +- logging.debug('running command: %s', cmd) +- +- argv = shlex.split(cmd) +- +- pid, master_fd = pty.fork() +- if pid == 0: +- os.execlp(argv[0], *argv) +- +- time.sleep(0.1) # FIXME: why is this necessary? +- output = '' +- search_position = 0 +- cur_prompt = 0 +- if cur_prompt < len(prompts): +- prompt_re = re.compile(prompts[cur_prompt][0]) +- response = prompts[cur_prompt][1] +- cur_prompt += 1 ++ if options.noise_filename is None: ++ fd, options.noise_filename = tempfile.mkstemp() ++ os.write(fd, random_data) ++ os.close(fd) + else: +- prompt_re = None +- response = None +- +- while True: +- try: +- new_data = os.read(master_fd, 1024) +- except OSError, e: +- if e.errno == errno.EIO: # process exited +- break +- else: +- raise +- if len(new_data) == 0: +- break # EOF +- output += new_data +- logging.debug('output="%s"', output[search_position:]); +- if prompt_re is not None: +- logging.debug('search pattern = "%s"', prompt_re.pattern) +- match = prompt_re.search(output, search_position) +- if match: +- search_position = match.end() +- parsed = output[match.start() : match.end()] +- logging.debug('found prompt: "%s"', parsed) +- logging.debug('writing response: "%s"', response) +- os.write(master_fd, response) +- +- if cur_prompt < len(prompts): +- prompt_re = re.compile(prompts[cur_prompt][0]) +- response = prompts[cur_prompt][1] +- cur_prompt += 1 +- else: +- prompt_re = None +- response = None +- +- +- exit_value = os.waitpid(pid, 0)[1] +- exit_signal = exit_value & 0xFF +- exit_code = exit_value >> 8 +- #logging.debug('output="%s"' % output) +- logging.debug('cmd signal=%s, exit_code=%s' % (exit_signal, exit_code)) +- +- return exit_code, output ++ with open(options.noise_filename, 'w') as f: ++ f.write(random_data) ++ return + ++def create_passwd_file(options): ++ fd, options.passwd_filename = tempfile.mkstemp() ++ os.write(fd, options.db_passwd) ++ os.close(fd) + +-#------------------------------------------------------------------------------- + +-def setup_certs(): +- print 'setting up certs ...' +- +- if os.path.exists(config['dbdir']): +- shutil.rmtree(config['dbdir']) +- os.makedirs(config['dbdir']) ++def db_has_cert(options, nickname): ++ cmd_args = ['/usr/bin/certutil', ++ '-d', options.db_name, ++ '-L', ++ '-n', nickname] + + try: ++ run_cmd(cmd_args) ++ except CmdError as e: ++ if e.returncode == 255 and 'not found' in e.stderr: ++ return False ++ else: ++ raise ++ return True ++ ++def format_cert(options, nickname): ++ cmd_args = ['/usr/bin/certutil', ++ '-L', # OPERATION: list ++ '-d', options.db_name, # NSS database ++ '-f', options.passwd_filename, # database password in file ++ '-n', nickname, # nickname of cert to list ++ ] + +- create_noise_file() +- +- # 1. Create the database +- cmd = 'certutil -N -d %(dbdir)s' % config +- exit_code, output = run_cmd_with_prompts(cmd, +- [('Enter new password:\s*', config['db_passwd'] + '\n'), +- ('Re-enter password:\s*', config['db_passwd'] + '\n')]) +- if exit_code != 0: +- raise CmdError(cmd, exit_code, output) +- +- # 2. Create a root CA certificate +- config['serial_number'] = next_serial() +- cmd = 'certutil -S -d %(dbdir)s -z %(noise_file)s -s "%(ca_subject)s" -n "%(ca_nickname)s" -x -t "CTu,C,C" -m %(serial_number)d' % config +- exit_code, output = run_cmd_with_prompts(cmd, +- [('Enter Password or Pin for "NSS Certificate DB":\s*', config['db_passwd'] + '\n')]) +- if exit_code != 0: +- raise CmdError(cmd, exit_code, output) +- +- # 3. Create a server certificate and sign it. +- config['serial_number'] = next_serial() +- cmd = 'certutil -S -d %(dbdir)s -z %(noise_file)s -c %(ca_nickname)s -s "%(server_subject)s" -n "%(server_nickname)s" -t "u,u,u" -m %(serial_number)d' % config +- exit_code, output = run_cmd_with_prompts(cmd, +- [('Enter Password or Pin for "NSS Certificate DB":\s*', config['db_passwd'] + '\n')]) +- if exit_code != 0: +- raise CmdError(cmd, exit_code, output) +- +- # 4. Create a client certificate and sign it. +- config['serial_number'] = next_serial() +- cmd = 'certutil -S -d %(dbdir)s -z %(noise_file)s -c %(ca_nickname)s -s "%(client_subject)s" -n "%(client_nickname)s" -t "u,u,u" -m %(serial_number)d' % config +- exit_code, output = run_cmd_with_prompts(cmd, +- [('Enter Password or Pin for "NSS Certificate DB":\s*', config['db_passwd'] + '\n')]) +- if exit_code != 0: +- raise CmdError(cmd, exit_code, output) +- +- # 5. Import public root CA's +- cmd = 'modutil -dbdir %(dbdir)s -add ca_certs -libfile libnssckbi.so' % config +- exit_code, stdout, stderr = run_cmd(cmd) +- if exit_code != 0: +- raise CmdError(cmd, exit_code, output) +- +- # 6. Create a sub CA certificate +- config['serial_number'] = next_serial() +- config['subca_subject'] = 'CN=subca' +- config['subca_nickname'] = 'subca' +- cmd = 'certutil -S -d %(dbdir)s -z %(noise_file)s -c %(ca_nickname)s -s "%(subca_subject)s" -n "%(subca_nickname)s" -t "CTu,C,C" -m %(serial_number)d' % config +- exit_code, output = run_cmd_with_prompts(cmd, +- [('Enter Password or Pin for "NSS Certificate DB":\s*', config['db_passwd'] + '\n')]) +- if exit_code != 0: +- raise CmdError(cmd, exit_code, output) +- +- # 7. Create a server certificate and sign it with the subca. +- config['serial_number'] = next_serial() +- config['server_subject'] = config['server_subject'] + "_" + config['subca_nickname'] +- config['server_nickname'] = config['server_nickname'] + "_" + config['subca_nickname'] +- cmd = 'certutil -S -d %(dbdir)s -z %(noise_file)s -c %(subca_nickname)s -s "%(server_subject)s" -n "%(server_nickname)s" -t "u,u,u" -m %(serial_number)d' % config +- exit_code, output = run_cmd_with_prompts(cmd, +- [('Enter Password or Pin for "NSS Certificate DB":\s*', config['db_passwd'] + '\n')]) +- if exit_code != 0: +- raise CmdError(cmd, exit_code, output) +- +- finally: +- if os.path.exists(config['noise_file']): +- os.remove(config['noise_file']) +- +- logging.info('certifcate database password="%(db_passwd)s"', config) +- logging.info('CA nickname="%(ca_nickname)s", CA subject="%(ca_subject)s"', config) +- logging.info('server nickname="%(server_nickname)s", server subject="%(server_subject)s"', config) +- logging.info('client nickname="%(client_nickname)s", client subject="%(client_subject)s"', config) ++ stdout, stderr = run_cmd(cmd_args) ++ return stdout + + #------------------------------------------------------------------------------- + +-class Usage(Exception): +- def __init__(self, msg): +- self.msg = msg ++def create_database(options): ++ if os.path.exists(options.db_dir) and not os.path.isdir(options.db_dir): ++ raise ValueError('db_dir "%s" exists but is not a directory' % options.db_dir) ++ ++ # Create resources ++ create_passwd_file(options) ++ ++ if options.clean: ++ logging.info('Creating clean database directory: "%s"', options.db_dir) ++ ++ if os.path.exists(options.db_dir): ++ shutil.rmtree(options.db_dir) ++ os.makedirs(options.db_dir) ++ ++ cmd_args = ['/usr/bin/certutil', ++ '-N', # OPERATION: create database ++ '-d', options.db_name, # NSS database ++ '-f', options.passwd_filename, # database password in file ++ ] + +-def usage(): +- ''' +- Print command help. +- ''' ++ stdout, stderr = run_cmd(cmd_args) ++ else: ++ logging.info('Using existing database directory: "%s"', options.db_dir) ++ ++def create_ca_cert(options): ++ serial_number = read_serial(options) ++ init_noise_file(options) ++ ++ logging.info('creating ca cert: subject="%s", nickname="%s"', ++ options.ca_subject, options.ca_nickname) ++ ++ cmd_args = ['/usr/bin/certutil', ++ '-S', # OPERATION: create signed cert ++ '-x', # self-sign the cert ++ '-d', options.db_name, # NSS database ++ '-f', options.passwd_filename, # database password in file ++ '-n', options.ca_nickname, # nickname of cert being created ++ '-s', options.ca_subject, # subject of cert being created ++ '-g', str(options.key_size), # keysize ++ '-t', 'CT,,CT', # trust ++ '-1', # add key usage extension ++ '-2', # add basic contraints extension ++ '-5', # add certificate type extension ++ '-m', str(serial_number), # cert serial number ++ '-v', str(options.valid_months), # validity in months ++ '-z', options.noise_filename, # noise file random seed ++ ] ++ ++ # Provide input for extension creation prompting ++ input = '' ++ ++ # >> Key Usage extension << ++ # 0 - Digital Signature ++ # 1 - Non-repudiation ++ # 2 - Key encipherment ++ # 3 - Data encipherment ++ # 4 - Key agreement ++ # 5 - Cert signing key ++ # 6 - CRL signing key ++ # Other to finish ++ input += '0\n1\n5\n100\n' ++ # Is this a critical extension [y/N]? ++ input += 'y\n' ++ ++ # >> Basic Constraints extension << ++ # Is this a CA certificate [y/N]? ++ input += 'y\n' ++ # Enter the path length constraint, enter to skip [<0 for unlimited path]: > 2 ++ input += '%d\n' % options.ca_path_len ++ # Is this a critical extension [y/N]? ++ input += 'y\n' ++ ++ # >> NS Cert Type extension << ++ # 0 - SSL Client ++ # 1 - SSL Server ++ # 2 - S/MIME ++ # 3 - Object Signing ++ # 4 - Reserved for future use ++ # 5 - SSL CA ++ # 6 - S/MIME CA ++ # 7 - Object Signing CA ++ # Other to finish ++ input += '5\n6\n7\n100\n' ++ # Is this a critical extension [y/N]? ++ input += 'n\n' ++ ++ stdout, stderr = run_cmd(cmd_args, input) ++ write_serial(options, serial_number + 1) ++ ++ return options.ca_nickname ++ ++def create_server_cert(options): ++ serial_number = read_serial(options) ++ init_noise_file(options) ++ ++ logging.info('creating server cert: subject="%s", nickname="%s"', ++ options.server_subject, options.server_nickname) ++ ++ cmd_args = ['/usr/bin/certutil', ++ '-S', # OPERATION: create signed cert ++ '-d', options.db_name, # NSS database ++ '-f', options.passwd_filename, # database password in file ++ '-c', options.ca_nickname, # nickname of CA used to sign this cert ++ '-n', options.server_nickname, # nickname of cert being created ++ '-s', options.server_subject, # subject of cert being created ++ '-g', str(options.key_size), # keysize ++ '-t', 'u,u,u', # trust ++ '-5', # add certificate type extensionn ++ '-m', str(serial_number), # cert serial number ++ '-v', str(options.valid_months), # validity in months ++ '-z', options.noise_filename, # noise file random seed ++ ] ++ ++ # Provide input for extension creation prompting ++ input = '' ++ ++ # >> NS Cert Type extension << ++ # 0 - SSL Client ++ # 1 - SSL Server ++ # 2 - S/MIME ++ # 3 - Object Signing ++ # 4 - Reserved for future use ++ # 5 - SSL CA ++ # 6 - S/MIME CA ++ # 7 - Object Signing CA ++ # Other to finish ++ input += '1\n100\n' ++ # Is this a critical extension [y/N]? ++ input += 'n\n' ++ ++ stdout, stderr = run_cmd(cmd_args, input) ++ write_serial(options, serial_number + 1) ++ ++ return options.server_nickname ++ ++def create_client_cert(options): ++ serial_number = read_serial(options) ++ init_noise_file(options) ++ ++ logging.info('creating client cert: subject="%s", nickname="%s"', ++ options.client_subject, options.client_nickname) ++ ++ cmd_args = ['/usr/bin/certutil', ++ '-S', # OPERATION: create signed cert ++ '-d', options.db_name, # NSS database ++ '-f', options.passwd_filename, # database password in file ++ '-c', options.ca_nickname, # nickname of CA used to sign this cert ++ '-n', options.client_nickname, # nickname of cert being created ++ '-s', options.client_subject, # subject of cert being created ++ '-g', str(options.key_size), # keysize ++ '-t', 'u,u,u', # trust ++ '-5', # add certificate type extensionn ++ '-m', str(serial_number), # cert serial number ++ '-v', str(options.valid_months), # validity in months ++ '-z', options.noise_filename, # noise file random seed ++ ] ++ ++ # Provide input for extension creation prompting ++ input = '' ++ ++ # >> NS Cert Type extension << ++ # 0 - SSL Client ++ # 1 - SSL Server ++ # 2 - S/MIME ++ # 3 - Object Signing ++ # 4 - Reserved for future use ++ # 5 - SSL CA ++ # 6 - S/MIME CA ++ # 7 - Object Signing CA ++ # Other to finish ++ input += '0\n100\n' ++ # Is this a critical extension [y/N]? ++ input += 'n\n' ++ ++ stdout, stderr = run_cmd(cmd_args, input) ++ write_serial(options, serial_number + 1) ++ ++ return options.client_nickname ++ ++def add_trusted_certs(options): ++ name = 'ca_certs' ++ module = 'libnssckbi.so' ++ logging.info('adding system trusted certs: name="%s" module="%s"', ++ name, module) ++ ++ cmd_args = ['/usr/bin/modutil', ++ '-dbdir', options.db_name, # NSS database ++ '-add', name, # module name ++ '-libfile', module, # module ++ ] + +- return '''\ +--h --help print help +--l --log-level level set the logging level, may be one of: +- debug, info, warn, error, critical +--L --logfile filename log to this file, empty string disables logging to a file +--v --verbose be chatty +--D --debug show run information +--w --password set the certificate database password +--d --dbdir set the datbase directory +--s --server-subject set the server's subject +- +-Examples: +- +-%(prog_name)s -m 10 +-''' % {'prog_name' : prog_name, +- } ++ run_cmd(cmd_args) ++ return name + + #------------------------------------------------------------------------------- + +-def main(argv=None): +- if argv is None: +- argv = sys.argv ++def setup_certs(args): + +- try: +- try: +- opts, args = getopt.getopt(argv[1:], 'hl:L:vDw:d:s:', +- ['help', 'logfile=', 'verbose', 'debug', +- 'password', 'dbdir', 'server-subject']) +- except getopt.GetoptError, e: +- raise Usage(e) +- return 2 +- +- for o, a in opts: +- if o in ('-h', '--help'): +- print >>sys.stdout, usage() +- return 0 +- elif o in ('-L', '--logfile'): +- if not a: +- config['logfile'] = None +- else: +- config['logfile'] = a +- elif o in ('-l', '--log-level'): +- if a.upper() in logging._levelNames: +- config['log_level'] = logging._levelNames[a.upper()] +- else: +- print >>sys.stderr, "ERROR: unknown log-level '%s'" % a +- elif o in ('-v', '--verbose'): +- config['verbose'] = True +- elif o in ('-D', '--debug'): +- config['debug'] = True +- elif o in ('-w', '--password'): +- config['db_passwd'] = a +- elif o in ('-d', '--dbdir'): +- config['dbdir'] = a +- elif o in ('-s', '--server-subject'): +- config['server_subject'] = 'CN=%s' % a +- else: +- raise Usage("command argument '%s' not handled, internal error" % o) +- except Usage, e: +- print >>sys.stderr, e.msg +- print >>sys.stderr, "for help use --help" +- return 2 +- +- if config['verbose']: +- config['log_level'] = logging.INFO +- if config['debug']: +- config['log_level'] = logging.DEBUG ++ # --- cmd --- ++ parser = argparse.ArgumentParser(description='create certs for testing', ++ formatter_class=argparse.ArgumentDefaultsHelpFormatter) ++ ++ parser.add_argument('--verbose', action='store_true', ++ help='provide info level messages') ++ ++ parser.add_argument('--debug', action='store_true', ++ help='provide debug level messages') ++ ++ parser.add_argument('--quiet', action='store_true', ++ help='do not display any messages') ++ ++ parser.add_argument('--show-certs', action='store_true', ++ help='show the certificate details') ++ ++ parser.add_argument('--no-clean', action='store_false', dest='clean', ++ help='do not remove existing db_dir') ++ ++ parser.add_argument('--no-trusted-certs', dest='add_trusted_certs', action='store_false', ++ help='do not add trusted certs') ++ ++ parser.add_argument('--hostname', ++ help='hostname used in cert subjects') ++ ++ parser.add_argument('--db-type', ++ choices=['sql', ''], ++ help='NSS database type') ++ ++ parser.add_argument('--db-dir', ++ help='NSS database directory') ++ ++ parser.add_argument('--db-passwd', ++ help='NSS database password') ++ ++ parser.add_argument('--ca-subject', ++ help='CA certificate subject') ++ ++ parser.add_argument('--ca-nickname', ++ help='CA certificate nickname') ++ ++ parser.add_argument('--server-subject', ++ help='server certificate subject') ++ ++ parser.add_argument('--server-nickname', ++ help='server certificate nickname') ++ ++ parser.add_argument('--client-username', ++ help='client user name, used in client cert subject') ++ ++ parser.add_argument('--client-subject', ++ help='client certificate subject') ++ ++ parser.add_argument('--client-nickname', ++ help='client certificate nickname') ++ ++ parser.add_argument('--serial-number', type=int, ++ help='starting serial number for certificates') ++ ++ parser.add_argument('--valid-months', dest='valid_months', type=int, ++ help='validity period in months') ++ parser.add_argument('--path-len', dest='ca_path_len', type=int, ++ help='basic constraints path length') ++ parser.add_argument('--key-type', dest='key_type', ++ help='key type, either rsa or dsa') ++ parser.add_argument('--key-size', dest='key_size', ++ help='number of bits in key (must be multiple of 8)') ++ parser.add_argument('--serial-file', dest='serial_file', ++ help='name of file used to track next serial number') ++ ++ parser.set_defaults(verbose = False, ++ debug = False, ++ quiet = False, ++ show_certs = False, ++ clean = True, ++ add_trusted_certs = True, ++ hostname = os.uname()[1], ++ db_type = 'sql', ++ db_dir = 'pki', ++ db_passwd = 'db_passwd', ++ ca_subject = 'CN=Test CA', ++ ca_nickname = 'test_ca', ++ server_subject = 'CN=${hostname}', ++ server_nickname = 'test_server', ++ client_username = 'test_user', ++ client_subject = 'CN=${client_username}', ++ client_nickname = '${client_username}', ++ serial_number = 1, ++ key_type = 'rsa', ++ key_size = 1024, ++ valid_months = 12, ++ ca_path_len = 2, ++ serial_file = '${db_dir}/serial', ++ ) ++ ++ ++ options = parser.parse_args(args) ++ ++ # Do substitutions on option values. ++ # This is ugly because argparse does not expose an API which permits iterating over ++ # the contents of options nor a way to get the options as a dict, ugh :-( ++ # So we access options.__dict__ directly. ++ for key in options.__dict__.keys(): ++ # Assume options never begin with underscore ++ if key.startswith('_'): ++ continue ++ value = getattr(options, key) ++ # Can't substitue on non-string values ++ if not isinstance(value, basestring): ++ continue ++ # Don't bother trying to substitute if $ substitution character isn't present ++ if '$' not in value: ++ continue ++ setattr(options, key, Template(value).substitute(options.__dict__)) ++ ++ # Set up logging ++ log_level = logging.INFO ++ if options.quiet: ++ log_level = logging.ERROR ++ if options.verbose: ++ log_level = logging.INFO ++ if options.debug: ++ log_level = logging.DEBUG + + # Initialize logging +- logging.basicConfig(level=config['log_level'], +- format='%(asctime)s %(levelname)-8s %(message)s', +- datefmt='%m-%d %H:%M', +- filename=config['logfile'], +- filemode='a') +- +- if config['interactive']: +- # Create a seperate logger for the console +- console_logger = logging.StreamHandler() +- console_logger.setLevel(config['log_level']) +- # set a format which is simpler for console use +- formatter = logging.Formatter('%(message)s') +- console_logger.setFormatter(formatter) +- # add the handler to the root logger +- logging.getLogger('').addHandler(console_logger) ++ logging.basicConfig(level=log_level, format='%(levelname)s: %(message)s') ++ logger = logging.getLogger() ++ ++ # Synthesize some useful options derived from specified options ++ if options.db_type == '': ++ options.db_name = options.db_dir ++ else: ++ options.db_name = '%s:%s' % (options.db_type, options.db_dir) ++ options.passwd_filename = None ++ options.noise_filename = None ++ ++ # Set function to clean up on exit, bind fuction with options ++ def exit_handler_with_options(): ++ exit_handler(options) ++ atexit.register(exit_handler_with_options) ++ ++ cert_nicknames = [] + + try: +- setup_certs() +- except Exception, e: +- logging.error(traceback.format_exc()) +- logging.error(str(e)) ++ create_database(options) ++ cert_nicknames.append(create_ca_cert(options)) ++ cert_nicknames.append(create_server_cert(options)) ++ cert_nicknames.append(create_client_cert(options)) ++ if options.add_trusted_certs: ++ add_trusted_certs(options) ++ except CmdError as e: ++ logging.error(e.message) ++ logging.error(e.stderr) + return 1 + ++ if options.show_certs: ++ if logger.getEffectiveLevel() > logging.INFO: ++ logger.setLevel(logging.INFO) ++ for nickname in cert_nicknames: ++ logging.info('Certificate nickname "%s"\n%s', ++ nickname, format_cert(options, nickname)) ++ ++ logging.info('---------- Summary ----------') ++ logging.info('NSS database name="%s", password="%s"', ++ options.db_name, options.db_passwd) ++ logging.info('CA nickname="%s", CA subject="%s"', ++ options.ca_nickname, options.ca_subject) ++ logging.info('server nickname="%s", server subject="%s"', ++ options.server_nickname, options.server_subject) ++ logging.info('client nickname="%s", client subject="%s"', ++ options.client_nickname, options.client_subject) ++ + return 0 + + #------------------------------------------------------------------------------- + ++def main(): ++ return setup_certs(None) ++ + if __name__ == '__main__': + sys.exit(main()) +diff -N -u -r python-nss-0.14.0/test/test_client_server.py python-nss-0.14.1/test/test_client_server.py +--- python-nss-0.14.0/test/test_client_server.py 2013-04-30 13:00:23.000000000 -0400 ++++ python-nss-0.14.1/test/test_client_server.py 2013-10-08 09:35:53.000000000 -0400 +@@ -25,13 +25,12 @@ + password = 'db_passwd' + use_ssl = True + client_cert_action = NO_CLIENT_CERT +-certdir = os.path.join(os.path.dirname(sys.argv[0]), 'pki') ++db_name = 'sql:pki' + hostname = os.uname()[1] + server_nickname = 'test_server' + client_nickname = 'test_user' + port = 1234 + timeout_secs = 10 +-family = io.PR_AF_INET + sleep_time = 5 + + +@@ -142,7 +141,6 @@ + if info: print "client: using SSL" + ssl.set_domestic_policy() + +- valid_addr = False + # Get the IP Address of our server + try: + addr_info = io.AddrInfo(hostname) +@@ -151,8 +149,6 @@ + return + + for net_addr in addr_info: +- if family != io.PR_AF_UNSPEC: +- if net_addr.family != family: continue + net_addr.port = port + + if use_ssl: +@@ -180,26 +176,21 @@ + if verbose: print "client trying connection to: %s" % (net_addr) + sock.connect(net_addr, timeout=io.seconds_to_interval(timeout_secs)) + if verbose: print "client connected to: %s" % (net_addr) +- valid_addr = True + break + except Exception, e: + sock.close() + print >>sys.stderr, "client: connection to: %s failed (%s)" % (net_addr, e) + +- if not valid_addr: +- print >>sys.stderr, "Could not establish valid address for \"%s\" in family %s" % \ +- (hostname, io.addr_family_name(family)) +- return +- + # Talk to the server + try: + if info: print "client: sending \"%s\"" % (request) +- sock.send(request) +- buf = sock.recv(1024) ++ sock.send(request + '\n') # newline is protocol record separator ++ buf = sock.readline() + if not buf: + print >>sys.stderr, "client: lost connection" + sock.close() + return ++ buf = buf.rstrip() # remove newline record separator + if info: print "client: received \"%s\"" % (buf) + except Exception, e: + print >>sys.stderr, "client: %s" % e +@@ -228,15 +219,11 @@ + # ----------------------------------------------------------------------------- + + def server(): +- global family +- + if verbose: print "starting server:" + + # Initialize + # Setup an IP Address to listen on any of our interfaces +- if family == io.PR_AF_UNSPEC: +- family = io.PR_AF_INET +- net_addr = io.NetworkAddress(io.PR_IpAddrAny, port, family) ++ net_addr = io.NetworkAddress(io.PR_IpAddrAny, port) + + if use_ssl: + if info: print "server: using SSL" +@@ -290,15 +277,16 @@ + while True: + try: + # Handle the client connection +- buf = client_sock.recv(1024) ++ buf = client_sock.readline() # newline is protocol record separator + if not buf: + print >>sys.stderr, "server: lost lost connection to %s" % (client_addr) + break ++ buf = buf.rstrip() # remove newline record separator + + if info: print "server: received \"%s\"" % (buf) +- reply = "{%s}" % buf # echo ++ reply = "{%s}" % buf # echo embedded inside braces + if info: print "server: sending \"%s\"" % (reply) +- client_sock.send(reply) # echo ++ client_sock.send(reply + '\n') # send echo with record separator + + time.sleep(sleep_time) + client_sock.shutdown() +@@ -320,7 +308,7 @@ + def run_server(): + pid = os.fork() + if pid == 0: +- nss.nss_init(certdir) ++ nss.nss_init(db_name) + server() + nss.nss_shutdown() + time.sleep(sleep_time) +@@ -348,7 +336,7 @@ + + def test_ssl(self): + request = "foo" +- nss.nss_init(certdir) ++ nss.nss_init(db_name) + reply = client(request) + nss.nss_shutdown() + self.assertEqual("{%s}" % request, reply) +diff -N -u -r python-nss-0.14.0/test/test_ocsp.py python-nss-0.14.1/test/test_ocsp.py +--- python-nss-0.14.0/test/test_ocsp.py 2013-04-24 12:36:07.000000000 -0400 ++++ python-nss-0.14.1/test/test_ocsp.py 2013-10-08 09:09:18.000000000 -0400 +@@ -7,7 +7,7 @@ + import nss.nss as nss + from nss.error import NSPRError + +-certdir = 'pki' ++db_name = 'sql:pki' + + #------------------------------------------------------------------------------- + +@@ -16,7 +16,7 @@ + + class TestAPI(unittest.TestCase): + def setUp(self): +- nss.nss_init_read_write(certdir) ++ nss.nss_init_read_write(db_name) + self.certdb = nss.get_default_certdb() + + def tearDown(self): +diff -N -u -r python-nss-0.14.0/test/test_pkcs12.py python-nss-0.14.1/test/test_pkcs12.py +--- python-nss-0.14.0/test/test_pkcs12.py 2013-04-15 13:05:46.000000000 -0400 ++++ python-nss-0.14.1/test/test_pkcs12.py 2013-10-17 10:29:09.000000000 -0400 +@@ -2,6 +2,7 @@ + + import sys + import os ++import re + import subprocess + import shlex + import unittest +@@ -13,30 +14,55 @@ + #------------------------------------------------------------------------------- + + verbose = False +-certdir = 'pki' ++db_name = 'sql:pki' + db_passwd = 'db_passwd' +-pkcs12_file_password = 'pk12_passwd' ++pk12_passwd = 'pk12_passwd' + +-read_nickname = 'test_user' +-read_pkcs12_file = '%s.p12' % read_nickname +- +-write_export_file = False +-export_nickname = 'test_server' ++cert_nickname = 'test_user' ++pk12_filename = '%s.p12' % cert_nickname ++exported_pk12_filename = 'exported_%s' % pk12_filename + + #------------------------------------------------------------------------------- + +-def run_cmd(cmd): +- if verbose: print "running command: %s" % cmd +- +- args = shlex.split(cmd) +- subprocess.check_call(args, stdout=subprocess.PIPE) ++class CmdError(Exception): ++ def __init__(self, cmd_args, returncode, message=None, stdout=None, stderr=None): ++ self.cmd_args = cmd_args ++ self.returncode = returncode ++ if message is None: ++ self.message = 'Failed error=%s, ' % (returncode) ++ if stderr: ++ self.message += '"%s", ' % stderr ++ self.message += 'args=%s' % (cmd_args) ++ else: ++ self.message = message ++ self.stdout = stdout ++ self.stderr = stderr ++ ++ def __str__(self): ++ return self.message ++ ++ ++def run_cmd(cmd_args, input=None): ++ try: ++ p = subprocess.Popen(cmd_args, ++ stdin=subprocess.PIPE, ++ stdout=subprocess.PIPE, ++ stderr=subprocess.PIPE) ++ stdout, stderr = p.communicate(input) ++ returncode = p.returncode ++ if returncode != 0: ++ raise CmdError(cmd_args, returncode, ++ 'failed %s' % (', '.join(cmd_args)), ++ stdout, stderr) ++ return stdout, stderr ++ except OSError as e: ++ raise CmdError(cmd_args, e.errno, stderr=str(e)) + + #------------------------------------------------------------------------------- + + def password_callback(slot, retry): + return db_passwd + +-#------------------------------------------------------------------------------- + + def nickname_collision_callback(old_nickname, cert): + cancel = False +@@ -44,6 +70,51 @@ + return new_nickname, cancel + + ++def get_cert_der_from_db(nickname): ++ cmd_args = ['/usr/bin/certutil', ++ '-d', db_name, ++ '-L', ++ '-n', nickname] ++ ++ try: ++ stdout, stderr = run_cmd(cmd_args) ++ except CmdError as e: ++ if e.returncode == 255 and 'not found' in e.stderr: ++ return None ++ else: ++ raise ++ return stdout ++ ++def delete_cert_from_db(nickname): ++ cmd_args = ['/usr/bin/certutil', ++ '-d', db_name, ++ '-D', ++ '-n', nickname] ++ ++ run_cmd(cmd_args) ++ ++def create_pk12(nickname, filename): ++ cmd_args = ['/usr/bin/pk12util', ++ '-o', filename, ++ '-n', nickname, ++ '-d', db_name, ++ '-K', db_passwd, ++ '-W', pk12_passwd] ++ run_cmd(cmd_args) ++ ++def list_pk12(filename): ++ cmd_args = ['/usr/bin/pk12util', ++ '-l', filename, ++ '-W', pk12_passwd] ++ stdout, stderr = run_cmd(cmd_args) ++ return stdout ++ ++def strip_key_from_pk12_listing(text): ++ match = re.search(r'^Certificate:$', text, re.MULTILINE) ++ if not match: ++ raise ValueError('Could not file Key section in pk12 listing') ++ return text[match.start(0):] ++ + #------------------------------------------------------------------------------- + + def load_tests(loader, tests, pattern): +@@ -59,22 +130,23 @@ + + class TestPKCS12Decoder(unittest.TestCase): + def setUp(self): +- nss.nss_init_read_write(certdir) ++ nss.nss_init_read_write(db_name) + nss.set_password_callback(password_callback) + nss.pkcs12_set_nickname_collision_callback(nickname_collision_callback) + nss.pkcs12_enable_all_ciphers() ++ self.cert_der = get_cert_der_from_db(cert_nickname) ++ if self.cert_der is None: ++ raise ValueError('cert with nickname "%s" not in database "%s"' % (cert_nickname, db_name)) + + def tearDown(self): + nss.nss_shutdown() + + def test_read(self): + if verbose: print "test_read" +- cmd='pk12util -o %s -n %s -d pki -K %s -W %s' % \ +- (read_pkcs12_file, read_nickname, db_passwd, pkcs12_file_password) +- run_cmd(cmd) ++ create_pk12(cert_nickname, pk12_filename) + + slot = nss.get_internal_key_slot() +- pkcs12 = nss.PKCS12Decoder(read_pkcs12_file, pkcs12_file_password, slot) ++ pkcs12 = nss.PKCS12Decoder(pk12_filename, pk12_passwd, slot) + + self.assertEqual(len(pkcs12), 3) + cert_bag_count = 0 +@@ -102,33 +174,43 @@ + + def test_import(self): + if verbose: print "test_import" +- cmd='certutil -d pki -D -n %s' % (read_nickname) +- run_cmd(cmd) ++ delete_cert_from_db(cert_nickname) ++ self.assertEqual(get_cert_der_from_db(cert_nickname), None) + + slot = nss.get_internal_key_slot() +- pkcs12 = nss.PKCS12Decoder(read_pkcs12_file, pkcs12_file_password, slot) ++ pkcs12 = nss.PKCS12Decoder(pk12_filename, pk12_passwd, slot) + slot.authenticate() + pkcs12.database_import() ++ cert_der = get_cert_der_from_db(cert_nickname) ++ self.assertEqual(cert_der, self.cert_der) + + #------------------------------------------------------------------------------- + + class TestPKCS12Export(unittest.TestCase): + def setUp(self): +- nss.nss_init(certdir) ++ nss.nss_init(db_name) + nss.set_password_callback(password_callback) + nss.pkcs12_enable_all_ciphers() ++ self.cert_der = get_cert_der_from_db(cert_nickname) ++ if self.cert_der is None: ++ raise ValueError('cert with nickname "%s" not in database "%s"' % (cert_nickname, db_name)) + + def tearDown(self): + nss.nss_shutdown() + + def test_export(self): + if verbose: print "test_export" +- pkcs12_data = nss.pkcs12_export(export_nickname, pkcs12_file_password) +- if write_export_file: +- p12_file_path = os.path.join(os.path.dirname(sys.argv[0]), "%s.p12" % export_nickname) +- f = open(p12_file_path, 'w') ++ pkcs12_data = nss.pkcs12_export(cert_nickname, pk12_passwd) ++ with open(exported_pk12_filename, 'w') as f: + f.write(pkcs12_data) +- f.close() ++ ++ pk12_listing = list_pk12(pk12_filename) ++ pk12_listing = strip_key_from_pk12_listing(pk12_listing) ++ ++ exported_pk12_listing = list_pk12(exported_pk12_filename) ++ exported_pk12_listing = strip_key_from_pk12_listing(exported_pk12_listing) ++ ++ self.assertEqual(pk12_listing, exported_pk12_listing) + + if __name__ == '__main__': + unittest.main() +diff -N -u -r python-nss-0.14.0/test/util.py python-nss-0.14.1/test/util.py +--- python-nss-0.14.0/test/util.py 1969-12-31 19:00:00.000000000 -0500 ++++ python-nss-0.14.1/test/util.py 2013-06-13 09:58:32.000000000 -0400 +@@ -0,0 +1,40 @@ ++import sys ++import os ++from distutils.util import get_platform ++ ++def get_build_dir(): ++ ''' ++ Walk from the current directory up until a directory is found ++ which contains a regular file called "setup.py" and a directory ++ called "build". If found return the fully qualified path to ++ the build directory's platform specific directory, this is where ++ the architecture specific build produced by setup.py is located. ++ ++ There is no API in distutils to return the platform specific ++ directory so we use as much as distutils exposes, the rest was ++ determined by looking at the source code for distutils. ++ ++ If the build directory cannont be found in the tree None is returned. ++ ''' ++ cwd = os.getcwd() ++ path_components = cwd.split('/') ++ while (len(path_components)): ++ path = os.path.join('/', *path_components) ++ setup_path = os.path.join(path, 'setup.py') ++ build_path = os.path.join(path, 'build') ++ # Does this directory contain the file "setup.py" and the directory "build"? ++ if os.path.exists(setup_path) and os.path.exists(build_path) and \ ++ os.path.isfile(setup_path) and os.path.isdir(build_path): ++ # Found, return the path contentated with the architecture ++ # specific build directory ++ platform_specifier = "lib.%s-%s" % (get_platform(), sys.version[0:3]) ++ return os.path.join(build_path, platform_specifier) ++ ++ # Not found, ascend to parent directory and try again ++ path_components.pop() ++ ++ # Failed to find the build directory ++ return None ++ ++def insert_build_dir_into_path(): ++ sys.path.insert(0,get_build_dir()) diff --git a/SPECS/python-nss.spec b/SPECS/python-nss.spec new file mode 100644 index 0000000..d62c533 --- /dev/null +++ b/SPECS/python-nss.spec @@ -0,0 +1,852 @@ +# sitelib for noarch packages, sitearch for others (remove the unneeded one) +%{!?python_sitelib: %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} +%{!?python_sitearch: %global python_sitearch %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)")} + +%global build_api_doc 1 + +Name: python-nss +Version: 0.14.0 +Release: 3%{?dist} +Summary: Python bindings for Network Security Services (NSS) + +Group: Development/Languages +License: MPLv2.0 or GPLv2+ or LGPLv2+ +URL: ftp://ftp.mozilla.org/pub/mozilla.org/security/python-nss +Source0: ftp://ftp.mozilla.org/pub/mozilla.org/security/python-nss/releases/PYNSS_RELEASE_0_14_0/src/python-nss-%{version}.tar.bz2 +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) + +Patch1: python-nss-0.14.1.patch + + +%global docdir %{_docdir}/%{name}-%{version} + +# We don't want to provide private python extension libs +%{?filter_setup: +%filter_provides_in %{python_sitearch}/.*\.so$ +%filter_setup +} + +BuildRequires: python-devel +BuildRequires: python-setuptools-devel +BuildRequires: python-docutils +BuildRequires: nspr-devel +BuildRequires: nss-devel +BuildRequires: epydoc + +%description +This package provides Python bindings for Network Security Services +(NSS) and the Netscape Portable Runtime (NSPR). + +NSS is a set of libraries supporting security-enabled client and +server applications. Applications built with NSS can support SSL v2 +and v3, TLS, PKCS #5, PKCS #7, PKCS #11, PKCS #12, S/MIME, X.509 v3 +certificates, and other security standards. Specific NSS +implementations have been FIPS-140 certified. + +%package doc +Group: Documentation +Summary: API documentation and examples +Requires: %{name} = %{version}-%{release} + +%description doc +API documentation and examples + +%prep +%setup -q +%patch1 -p1 -b .0.14.1 + +%build +CFLAGS="$RPM_OPT_FLAGS -fno-strict-aliasing" %{__python} setup.py build +%if %build_api_doc +%{__python} setup.py build_doc +%endif + +%install +rm -rf $RPM_BUILD_ROOT +%{__python} setup.py install -O1 --install-platlib %{python_sitearch} --skip-build --root $RPM_BUILD_ROOT +%{__python} setup.py install_doc --docdir %{docdir} --skip-build --root $RPM_BUILD_ROOT + +# Remove execution permission from any example/test files in docdir +find $RPM_BUILD_ROOT/%{docdir} -type f | xargs chmod a-x + +# Set correct permissions on .so files +chmod 0755 $RPM_BUILD_ROOT/%{python_sitearch}/nss/*.so + + +%clean +rm -rf $RPM_BUILD_ROOT + + +%files +%defattr(-,root,root,-) +%{python_sitearch}/* +%doc %{docdir}/ChangeLog +%doc %{docdir}/LICENSE.gpl +%doc %{docdir}/LICENSE.lgpl +%doc %{docdir}/LICENSE.mpl +%doc %{docdir}/README + +%files doc +%defattr(-,root,root,-) +%doc %{docdir}/examples +%doc %{docdir}/test +%if %build_api_doc +%doc %{docdir}/api +%endif + +%changelog +* Fri Oct 18 2013 John Dennis - 0.14.0-3 +- resolves: bug#1003979 +- In coordination with QE with regards to bz 1019934 it was requested + the unittest patches be enhanced with a more robust version of + test_pkcs12, no actual bug, just better testing. + +* Tue Oct 8 2013 John Dennis - 0.14.0-2 +- resolves: bug#1002589 +- resolves: bug#1003979 + +- Rewrite setup_certs.py. No longer behaves like an expect script + which was fragile. By default now creates a sql style database. +- By default all examples & tests use new sql format for NSS database +- db-name is now used instead of dbdir to provide distinction between + the database directory and it's scheme (e.g. 'sql:') +- all examples and tests now default db-name to 'sql:pki' +- replaced legacy getopt & optparse command line argument handling + with modern argparse. + +* Mon May 13 2013 John Dennis - 0.14-1 + External Changes: + ----------------- + + The primary enhancements in this version is support of certifcate + validation, OCSP support, and support for the certificate "Authority + Information Access" extension. + + Enhanced certifcate validation including CA certs can be done via + Certificate.verify() or Certificate.is_ca_cert(). When cert + validation fails you can now obtain diagnostic information as to why + the cert failed to validate. This is encapsulated in the + CertVerifyLog class which is a iterable collection of + CertVerifyLogNode objects. Most people will probablby just print the + string representation of the returned CertVerifyLog object. Cert + validation logging is handled by the Certificate.verify() method. + Support has also been added for the various key usage and cert type + entities which feature prominently during cert validation. + + + * Certificate() constructor signature changed from + + Certificate(data=None, der_is_signed=True) + + to + + Certificate(data, certdb=cert_get_default_certdb(), perm=False, nickname=None) + + This change was necessary because all certs should be added to the + NSS temporary database when they are loaded, but earlier code + failed to to that. It's is not likely that an previous code was + failing to pass initialization data or the der_is_signed flag so + this change should be backwards compatible. + + * Fix bug #922247, PKCS12Decoder.database_import() method. Importing into + a NSS database would sometimes fail or segfault. + + * Error codes and descriptions were updated from upstream NSPR & NSS. + + * The password callback did not allow for breaking out of a password + prompting loop, now if None is returned from the password callback + the password prompting is terminated. + + * nss.nss_shutdown_context now called from InitContext destructor, + this assures the context is shutdown even if the programmer forgot + to. It's still best to explicitly shut it down, this is just + failsafe. + + * Support was added for shutdown callbacks. + + * The following classes were added: + - nss.CertVerifyLogNode + - nss.CertVerifyLog + - error.CertVerifyError (exception) + - nss.AuthorityInfoAccess + - nss.AuthorityInfoAccesses + + + * The following class methods were added: + - nss.Certificate.is_ca_cert + - nss.Certificate.verify + - nss.Certificate.verify_with_log + - nss.Certificate.get_cert_chain + - nss.Certificate.check_ocsp_status + - nss.PK11Slot.list_certs + - nss.CertVerifyLogNode.format_lines + - nss.CertVerifyLog.format_lines + - nss.CRLDistributionPts.format_lines + + * The following class properties were added: + - nss.CertVerifyLogNode.certificate + - nss.CertVerifyLogNode.error + - nss.CertVerifyLogNode.depth + - nss.CertVerifyLog.count + + * The following module functions were added: + - nss.x509_cert_type + - nss.key_usage_flags + - nss.list_certs + - nss.find_certs_from_email_addr + - nss.find_certs_from_nickname + - nss.nss_get_version + - nss.nss_version_check + - nss.set_shutdown_callback + - nss.get_use_pkix_for_validation + - nss.set_use_pkix_for_validation + - nss.enable_ocsp_checking + - nss.disable_ocsp_checking + - nss.set_ocsp_cache_settings + - nss.set_ocsp_failure_mode + - nss.set_ocsp_timeout + - nss.clear_ocsp_cache + - nss.set_ocsp_default_responder + - nss.enable_ocsp_default_responder + - nss.disable_ocsp_default_responder + + * The following files were added: + src/py_traceback.h + doc/examples/verify_cert.py + test/test_misc.py + + * The following constants were added: + - nss.KU_DIGITAL_SIGNATURE + - nss.KU_NON_REPUDIATION + - nss.KU_KEY_ENCIPHERMENT + - nss.KU_DATA_ENCIPHERMENT + - nss.KU_KEY_AGREEMENT + - nss.KU_KEY_CERT_SIGN + - nss.KU_CRL_SIGN + - nss.KU_ENCIPHER_ONLY + - nss.KU_ALL + - nss.KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION + - nss.KU_KEY_AGREEMENT_OR_ENCIPHERMENT + - nss.KU_NS_GOVT_APPROVED + - nss.PK11CertListUnique + - nss.PK11CertListUser + - nss.PK11CertListRootUnique + - nss.PK11CertListCA + - nss.PK11CertListCAUnique + - nss.PK11CertListUserUnique + - nss.PK11CertListAll + - nss.certUsageSSLClient + - nss.certUsageSSLServer + - nss.certUsageSSLServerWithStepUp + - nss.certUsageSSLCA + - nss.certUsageEmailSigner + - nss.certUsageEmailRecipient + - nss.certUsageObjectSigner + - nss.certUsageUserCertImport + - nss.certUsageVerifyCA + - nss.certUsageProtectedObjectSigner + - nss.certUsageStatusResponder + - nss.certUsageAnyCA + - nss.ocspMode_FailureIsVerificationFailure + - nss.ocspMode_FailureIsNotAVerificationFailure + + * cert_dump.py extended to print NS_CERT_TYPE_EXTENSION + + * cert_usage_flags, nss_init_flags now support optional repr_kind parameter + + Internal Changes: + ----------------- + + * Reimplement exception handling + - NSPRError is now derived from StandardException instead of + EnvironmentError. It was never correct to derive from + EnvironmentError but was difficult to implement a new subclassed + exception with it's own attributes, using EnvironmentError had + been expedient. + + - NSPRError now derived from StandardException, provides: + * errno (numeric error code) + * strerror (error description associated with error code) + * error_message (optional detailed message) + * error_code (alias for errno) + * error_desc (alias for strerror) + + - CertVerifyError derived from NSPRError, extends with: + * usages (bitmask of returned usages) + * log (CertVerifyLog object) + + * Expose error lookup to sibling modules + + * Use macros for bitmask_to_list functions to reduce code + duplication and centralize logic. + + * Add repr_kind parameter to cert_trust_flags_str() + + * Add support for repr_kind AsEnumName to bitstring table lookup. + + * Add cert_type_bitstr_to_tuple() lookup function + + * Add PRTimeConvert(), used to convert Python time values + to PRTime, centralizes conversion logic, reduces duplication + + * Add UTF8OrNoneConvert to better handle unicode parameters which + are optional. + + * Add Certificate_summary_format_lines() utility to generate + concise certificate identification info for output. + + * Certificate_new_from_CERTCertificate now takes add_reference parameter + to properly reference count certs, should fix shutdown busy problems. + + * Add print_traceback(), print_cert() debugging support. + +* Mon Feb 18 2013 John Dennis - 0.13-1 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Mon Oct 8 2012 John Dennis - 0.13-0 +- Update to version 0.13 + Introduced in 0.13: + + * Fix NSS SECITEM_CompareItem bug via workaround. + + * Fix incorrect format strings in PyArg_ParseTuple* for: + - GeneralName + - BasicConstraints + - cert_x509_key_usage + + * Fix bug when decoding certificate BasicConstraints extension + + * Fix hang in setup_certs. + + * For NSS >= 3.13 support CERTDB_TERMINAL_RECORD + + * You can now query for a specific certificate extension + Certficate.get_extension() + + * The following classes were added: + - RSAGenParams + + * The following class methods were added: + - nss.nss.Certificate.get_extension + - nss.nss.PK11Slot.generate_key_pair + - nss.nss.DSAPublicKey.format + - nss.nss.DSAPublicKey.format_lines + + * The following module functions were added: + - nss.nss.pub_wrap_sym_key + + * The following internal utilities were added: + - PyString_UTF8 + - SecItem_new_alloc() + + * The following class constructors were modified to accept + intialization parameters + + - KEYPQGParams (DSA generation parameters) + + * The PublicKey formatting (i.e. format_lines) was augmented + to format DSA keys (formerly it only recognized RSA keys). + + * Allow lables and values to be justified when printing objects + + * The following were deprecated: + - nss.nss.make_line_pairs (replaced by nss.nss.make_line_fmt_tuples) + + Deprecated Functionality: + ------------------------- + - make_line_pairs() has been replaced by make_line_fmt_tuples() + because 2-valued tuples were not sufficently general. It is + expected very few programs will have used this function, it's mostly + used internally but provided as a support utility. + +* Sat Jul 21 2012 Fedora Release Engineering - 0.12-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Sat Jan 14 2012 Fedora Release Engineering - 0.12-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Thu Nov 17 2011 John Dennis - 0.12-2 +- add patch python-nss-0.12-rsapssparams.patch to fix build problem + which appears only with nss 3.13 and later. + +* Mon Jun 6 2011 John Dennis - 0.12-1 + * Major new enhancement is additon of PKCS12 support and + AlgorithmID's. + + * setup.py build enhancements + - Now searches for the NSS and NSPR header files rather + than hardcoding their location. This makes building friendlier + on other systems (i.e. debian) + - Now takes optional command line arguments, -d or --debug + will turn on debug options during the build. + + * Fix reference counting bug in PK11_password_callback() which + contributed to NSS not being able to shutdown due to + resources still in use. + + * Add UTF-8 support to ssl.config_server_session_id_cache() + + * Added unit tests for cipher, digest, client_server. + + * All unittests now run, added test/run_tests to invoke + full test suite. + + * Fix bug in test/setup_certs.py, hardcoded full path to + libnssckbi.so was causing failures on 64-bit systems, + just use the libnssckbi.so basename, modutil will find + it on the standard search path. + + * doc/examples/cert_dump.py uses new AlgorithmID class to + dump Signature Algorithm + + * doc/examples/ssl_example.py now can cleanly shutdown NSS. + + * Exception error messages now include PR error text if available. + + * The following classes were replaced: + - SignatureAlgorithm replaced by new class AlgorithmID + + * The following classes were added: + - AlgorithmID + - PKCS12DecodeItem + - PKCS12Decoder + + * The following class methods were added: + - PK11Slot.authenticate() + - PK11Slot.get_disabled_reason() + - PK11Slot.has_protected_authentication_path() + - PK11Slot.has_root_certs() + - PK11Slot.is_disabled() + - PK11Slot.is_friendly() + - PK11Slot.is_internal() + - PK11Slot.is_logged_in() + - PK11Slot.is_removable() + - PK11Slot.logout() + - PK11Slot.need_login() + - PK11Slot.need_user_init() + - PK11Slot.user_disable() + - PK11Slot.user_enable() + - PKCS12DecodeItem.format() + - PKCS12DecodeItem.format_lines() + - PKCS12Decoder.database_import() + - PKCS12Decoder.format() + - PKCS12Decoder.format_lines() + + * The following class properties were added: + - AlgorithmID.id_oid + - AlgorithmID.id_str + - AlgorithmID.id_tag + - AlgorithmID.parameters + - PKCS12DecodeItem.certificate + - PKCS12DecodeItem.friendly_name + - PKCS12DecodeItem.has_key + - PKCS12DecodeItem.shroud_algorithm_id + - PKCS12DecodeItem.signed_cert_der + - PKCS12DecodeItem.type + - SignedData.data + - SignedData.der + + * The following module functions were added: + - nss.nss.dump_certificate_cache_info() + - nss.nss.find_slot_by_name() + - nss.nss.fingerprint_format_lines() + - nss.nss.get_internal_slot() + - nss.nss.is_fips() + - nss.nss.need_pw_init() + - nss.nss.nss_init_read_write() + - nss.nss.pk11_disabled_reason_name() + - nss.nss.pk11_disabled_reason_str() + - nss.nss.pk11_logout_all() + - nss.nss.pkcs12_cipher_from_name() + - nss.nss.pkcs12_cipher_name() + - nss.nss.pkcs12_enable_all_ciphers() + - nss.nss.pkcs12_enable_cipher() + - nss.nss.pkcs12_export() + - nss.nss.pkcs12_map_cipher() + - nss.nss.pkcs12_set_nickname_collision_callback() + - nss.nss.pkcs12_set_preferred_cipher() + - nss.nss.token_exists() + - nss.ssl.config_mp_server_sid_cache() + - nss.ssl.config_server_session_id_cache_with_opt() + - nss.ssl.get_max_server_cache_locks() + - nss.ssl.set_max_server_cache_locks() + - nss.ssl.shutdown_server_session_id_cache() + + * The following constants were added: + - nss.nss.int.PK11_DIS_COULD_NOT_INIT_TOKEN + - nss.nss.int.PK11_DIS_NONE + - nss.nss.int.PK11_DIS_TOKEN_NOT_PRESENT + - nss.nss.int.PK11_DIS_TOKEN_VERIFY_FAILED + - nss.nss.int.PK11_DIS_USER_SELECTED + - nss.nss.int.PKCS12_DES_56 + - nss.nss.int.PKCS12_DES_EDE3_168 + - nss.nss.int.PKCS12_RC2_CBC_128 + - nss.nss.int.PKCS12_RC2_CBC_40 + - nss.nss.int.PKCS12_RC4_128 + - nss.nss.int.PKCS12_RC4_40 + + * The following files were added: + - test/run_tests + - test/test_cipher.py (replaces cipher_test.py) + - test/test_client_server.py + - test/test_digest.py (replaces digest_test.py) + - test/test_pkcs12.py + + * The following were deprecated: + - SignatureAlgorithm + +* Tue Mar 22 2011 John Dennis - 0.11-2 +- Resolves: #689059 + Add family parameter to Socket constructors in examples and doc. + Mark implicit family parameter as deprecated. + Raise exception if Socket family does not match NetworkAddress family. + Add --server-subject to setup_certs.py (made testing IPv6 easier without DNS) + +* Mon Feb 21 2011 John Dennis - 0.11-1 + * Better support for IPv6 + + * Add AddrInfo class to support IPv6 address resolution. Supports + iteration over it's set of NetworkAddress objects and provides + hostname, canonical_name object properties. + + * Add PR_AI_* constants. + + * NetworkAddress constructor and NetworkAddress.set_from_string() added + optional family parameter. This is necessary for utilizing + PR_GetAddrInfoByName(). + + * NetworkAddress initialized via a string paramter are now initalized via + PR_GetAddrInfoByName using family. + + * Add NetworkAddress.address property to return the address sans the + port as a string. NetworkAddress.str() includes the port. For IPv6 the + a hex string must be enclosed in brackets if a port is appended to it, + the bracketed hex address with appended with a port is unappropriate + in some circumstances, hence the new address property to permit either + the address string with a port or without a port. + + * Fix the implementation of the NetworkAddress.family property, it was + returning bogus data due to wrong native data size. + + * HostEntry objects now support iteration and indexing of their + NetworkAddress members. + + * Add io.addr_family_name() function to return string representation of + PR_AF_* constants. + + * Modify example and test code to utilize AddrInfo instead of deprecated + NetworkAddress functionality. Add address family command argument to + ssl_example. + + * Fix pty import statement in test/setup_certs.py + + Deprecated Functionality: + ------------------------- + + * NetworkAddress initialized via a string paramter is now + deprecated. AddrInfo should be used instead. + + * NetworkAddress.set_from_string is now deprecated. AddrInfo should be + used instead. + + * NetworkAddress.hostentry is deprecated. It was a bad idea, + NetworkAddress objects can support both IPv4 and IPv6, but a HostEntry + object can only support IPv4. Plus the implementation depdended on + being able to perform a reverse DNS lookup which is not always + possible. + + * HostEntry.get_network_addresses() and HostEntry.get_network_address() + are now deprecated. In addition their port parameter is now no longer + respected. HostEntry objects now support iteration and + indexing of their NetworkAddress and that should be used to access + their NetworkAddress objects instead. + +* Tue Feb 08 2011 Fedora Release Engineering - 0.10-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Tue Jan 11 2011 John Dennis - 0.10-3 +- Fix all rpmlint warnings +- doc for license, changelog etc. now in main package, + doc subpackage now only contains api doc, examples, test, etc. +- Filter provides for .so files +- Remove execute permission on everything in docdir +- Capitalize description + +* Tue Jan 11 2011 John Dennis - 0.10-2 +- split documentation out into separate doc sub-package + and make building api documentation optional + +* Mon Jan 10 2011 John Dennis - 0.10-1 +- The following classes were added: + InitParameters + InitContext + +-The following module functions were added: + nss.nss.nss_initialize() + nss.nss.nss_init_context() + nss.nss.nss_shutdown_context() + nss.nss.nss_init_flags() + +* Thu Jul 22 2010 David Malcolm - 0.9-9 +- Rebuilt for https://fedoraproject.org/wiki/Features/Python_2.7/MassRebuild + +* Fri Jul 16 2010 John Dennis - 0.9-8 +- add nss_is_initialized() + +* Thu Jul 8 2010 John Dennis - 0.9-7 +- Remove nss_init_nodb() when nss modules loads from previous version + apparently this prevents subsequent calls to nss_init with a + database to silently fail. +- Clean up some cruft in doc/examples/verify_server.py + +* Thu Jun 24 2010 John Dennis - 0.9-6 +- Invoke nss_init_nodb() when nss modules loads, this prevents segfaults + in NSS if Python programmer forgot to call one of the NSS + initialization routines. + +- Rename the classes X500Name, X500RDN, X500AVA to DN, RDN, AVA + respectively. + +- DN and RDN objects now return a list of their contents when indexed by + type, this is to support multi-valued items. + +- Fix bug where AVA object's string representation did not include it's + type. + +- Enhance test/test_cert_components.py unit test to test for above + changes. + +- Add CertificateRequest object + +* Mon Jun 14 2010 John Dennis - 0.9-5 +- Fix incomplete read bug (due to read ahead buffer bookkeeping). +- Remove python-nss specific httplib.py, no longer needed + python-nss now compatible with standard library +- Rewrite httplib_example.py to use standard library and illustrate + ssl, non-ssl, connection class, http class usage + +* Wed Jun 9 2010 John Dennis - 0.9-4 +- add nss.cert_usage_flags(), use it in ssl_example.py + +* Sun Jun 6 2010 John Dennis - 0.9-3 +- Add format_lines() & format() methods to the new certificate extension objects. +- Add printing of certificate extensions. +- Add BasicContstraints certificate extension. +- Fix several reference counting and memory problems discovered with valgrind. + +* Tue Jun 1 2010 John Dennis - 0.9-2 +- fold in more ref counting patches from Miloslav Trmač + into upstream. + Did not bump upstream version, just bumped release ver in this spec file. + +* Fri May 28 2010 John Dennis - 0.9-1 +- Unicode objects now accepted as well as str objects for + interfaces expecting a string. + +- Sockets were enhanced thusly: + - Threads will now yield during blocking IO. + - Socket.makefile() reimplemented + file object methods that had been missing (readlines(), sendall(), + and iteration) were implemented, makefile now just returns the same + Socket object but increments an "open" ref count. Thus a Socket + object behaves like a file object and must be closed once for each + makefile() call before it's actually closed. + - Sockets now support the iter protocol + - Add Socket.readlines(), Socket.sendall() + +- The following classes were added: + AuthKeyID + BasicConstraints + CRLDistributionPoint + CRLDistributionPts + CertificateExtension + GeneralName + SignedCRL + X500AVA + X500Name + X500RDN + +- The following module functions were added: + nss.nss.cert_crl_reason_from_name() + nss.nss.cert_crl_reason_name() + nss.nss.cert_general_name_type_from_name() + nss.nss.cert_general_name_type_name() + nss.nss.cert_usage_flags() + nss.nss.decode_der_crl() + nss.nss.der_universal_secitem_fmt_lines() + nss.nss.import_crl() + nss.nss.make_line_pairs() + nss.nss.oid_dotted_decimal() + nss.nss.oid_str() + nss.nss.oid_tag() + nss.nss.oid_tag_name() + nss.nss.read_der_from_file() + nss.nss.x509_alt_name() + nss.nss.x509_ext_key_usage() + nss.nss.x509_key_usage() + +- The following class methods and properties were added: + Note: it's a method if the name is suffixed with (), a propety otherwise + Socket.next() + Socket.readlines() + Socket.sendall() + SSLSocket.next() + SSLSocket.readlines() + SSLSocket.sendall() + AuthKeyID.key_id + AuthKeyID.serial_number + AuthKeyID.get_general_names() + CRLDistributionPoint.issuer + CRLDistributionPoint.get_general_names() + CRLDistributionPoint.get_reasons() + CertDB.find_crl_by_cert() + CertDB.find_crl_by_name() + Certificate.extensions + CertificateExtension.critical + CertificateExtension.name + CertificateExtension.oid + CertificateExtension.oid_tag + CertificateExtension.value + GeneralName.type_enum + GeneralName.type_name + GeneralName.type_string + SecItem.der_to_hex() + SecItem.get_oid_sequence() + SecItem.to_hex() + SignedCRL.delete_permanently() + X500AVA.oid + X500AVA.oid_tag + X500AVA.value + X500AVA.value_str + X500Name.cert_uid + X500Name.common_name + X500Name.country_name + X500Name.dc_name + X500Name.email_address + X500Name.locality_name + X500Name.org_name + X500Name.org_unit_name + X500Name.state_name + X500Name.add_rdn() + X500Name.has_key() + X500RDN.has_key() + +- The following module functions were removed: + Note: use nss.nss.oid_tag() instead + nss.nss.sec_oid_tag_from_name() + nss.nss.sec_oid_tag_name() + nss.nss.sec_oid_tag_str() + +- The following files were added: + doc/examples/cert_dump.py + test/test_cert_components.py + +- Apply patches from Miloslav Trmač + for ref counting and threading support. Thanks Miloslav! + +- Review all ref counting, numerous ref counting fixes + +- Implement cyclic garbage collection support by + adding object traversal and clear methods + +- Identify static variables, move to thread local storage + + +* Wed Mar 24 2010 John Dennis - 0.8-2 +- change %%define to %%global + +* Mon Sep 21 2009 John Dennis - 0.8-1 +- The following methods, properties and functions were added: + SecItem.type SecItem.len, SecItem.data + PK11SymKey.key_data, PK11SymKey.key_length, PK11SymKey.slot + create_context_by_sym_key + param_from_iv + generate_new_param + get_iv_length + get_block_size + get_pad_mechanism +- SecItem's now support indexing and slicing on their data +- Clean up parsing and parameter validation of variable arg functions + +* Fri Sep 18 2009 John Dennis - 0.7-1 +- add support for symmetric encryption/decryption + more support for digests (hashes) + + The following classes were added: + PK11SymKey PK11Context + + The following methods and functions were added: + get_best_wrap_mechanism get_best_key_length + key_gen derive + get_key_length digest_key + clone_context digest_begin + digest_op cipher_op + finalize digest_final + read_hex hash_buf + sec_oid_tag_str sec_oid_tag_name + sec_oid_tag_from_name key_mechanism_type_name + key_mechanism_type_from_name pk11_attribute_type_name + pk11_attribute_type_from_name get_best_slot + get_internal_key_slot create_context_by_sym_key + import_sym_key create_digest_context + param_from_iv param_from_algid + generate_new_param algtag_to_mechanism + mechanism_to_algtag + + The following files were added: + cipher_test.py digest_test.py + +* Sun Jul 26 2009 Fedora Release Engineering - 0.6-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild + +* Thu Jul 9 2009 John Dennis - 0.6-2 +- restore nss.nssinit(), make deprecated + +* Wed Jul 8 2009 John Dennis - 0.6-1 +- fix bug #510343 client_auth_data_callback seg faults if False + is returned from callback + +* Wed Jul 1 2009 John Dennis - 0.5-1 +- restore ssl.nss_init and ssl.nss_shutdown but make them deprecated + add __version__ string to nss module + +* Tue Jun 30 2009 John Dennis - 0.4-1 +- add binding for NSS_NoDB_Init(), bug #509002 + move nss_init and nss_shutdown from ssl module to nss module + +* Thu Jun 4 2009 John Dennis - 0.3-1 +- installed source code in Mozilla CVS repository + update URL tag to point to CVS repositoy + (not yet a valid URL, still have to coordinate with Mozilla) + minor tweak to src directory layout + +* Mon Jun 1 2009 John Dennis - 0.2-1 +- Convert licensing to MPL tri-license +- apply patch from bug #472805, (Miloslav Trmač) + Don't allow closing a socket twice, that causes crashes. + New function nss.io.Socket.new_socket_pair() + New function nss.io.Socket.poll() + New function nss.io.Socket.import_tcp_socket() + New method nss.nss.Certificate.get_subject_common_name() + New function nss.nss.generate_random() + Fix return value creation in SSLSocket.get_security_status + New function nss.ssl.SSLSocket.import_tcp_socket() + +* Thu Feb 26 2009 Fedora Release Engineering - 0.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild + +* Sat Nov 29 2008 Ignacio Vazquez-Abrams - 0.1-2 +- Rebuild for Python 2.6 + +* Tue Sep 9 2008 John Dennis - 0.1-1 +- clean up ssl_example.py, fix arg list in get_cert_nicknames, + make certdir cmd line arg consistent with other NSS tools +- update httplib.py to support client auth, add httplib_example.py which illustrates it's use +- fix some documentation +- fix some type usage which were unsafe on 64-bit + +* Wed Jul 9 2008 John Dennis - 0.0-2 +- add docutils to build requires so restructured text works + +* Fri Jun 27 2008 John Dennis - 0.0-1 +- initial release