Package nss
source code
============
Introduction
============
This package provides a binding for the Network Security Services
(NSS) library. Because NSS directly uses the Netscape Portable Runtime
(NSPR) the binding also provides support for NSPR. There is an
inherent conflict between NSPR and Python, please see the Issues
section for more detail.
General documentation on NSS can be found here:
http://www.mozilla.org/projects/security/pki/nss
General documentation on NSPR can be found here:
http://developer.mozilla.org/en/docs/NSPR_API_Reference
Please note, the documentation included with this package already
encapsultes most of the information at the above two URL's, but is
specific to the python binding of NSS/NSPR. It is suggested you refer
to the python-nss documentation.
Most of the names and symbols in the NSS/NSPR C API have been kept in
the nss-python binding and should be instantly familar or
recognizable. Python has different naming conventions and the
nss-python binding has adhered to the python naming convensions,
Classes are camel case, otherwise symbols are all lower case with
words seperated by underscores. The constants used by NSS/NSPR in C
API have been imported literally to add the programmer who might be
referring to the Mozilla NSS/NSPR documentation and/or header files or
who is porting an existing C application to python. Minor other
changes have been made in the interest of being "Pythonic".
========================
Deprecated Functionality
========================
Some elements of the binding have been deprecated because of lessons
learned along the way. The following emit deprecation warnings and
should not be used, they will be removed in a subsequent release.
`io.NetworkAddress()`
`NetworkAddress` initialization from a string parameter only works
for IPv4, use `AddrInfo` instead.
`io.NetworkAddress.set_from_string()`
`NetworkAddress` initialization from a string parameter only works
for IPv4, use `AddrInfo` instead.
`io.NetworkAddress.hostentry`
`HostEntry` objects only support IPv4, this property will be
removed, use `AddrInfo` instead.
`io.HostEntry.get_network_addresses()`
Use iteration instead (e.g. for net_adder in hostentry), the port
parameter is not respected, port will be value when `HostEntry`
object was created.
`io.HostEntry.get_network_address()`
Use indexing instead (e.g. hostentry[i]), the port parameter is
not respected, port will be value when `HostEntry` object was
created.
`ssl.nssinit()`
nssinit has been moved to the nss module, use `nss.nss_init()`
instead of ssl.nssinit
`ssl.nss_init()`
nss_init has been moved to the nss module, use `nss.nss_init()`
instead of ssl.nssinit
`ssl.nss_shutdown()`
nss_shutdown() has been moved to the nss module, use
`nss.nss_shutdown()` instead of ssl.nss_shutdown()
`io.Socket()` and `ssl.SSLSocket()` without explicit family parameter
Socket initialization will require the family parameter in the future.
The default family parameter of PR_AF_INET is deprecated because
when iterating through `NetworkAddress` objects returned by
`AddrInfo` some address may be an IPv6 address. Suggest using the
family property of the NetworkAddress object associated with the
socket, e.g. Socket(net_addr.family)
===============
Getting Started
===============
NSS stores it's certificates and private keys in a security database
unlike OpenSSL which references it's certificates and keys via file
pathnames. This means unless you already have an NSS Certificate
Database (CertDB) the first order of business will be to create
one. When a NSS application initializes itself it will need to specify
the path to the CertDB (see "Things All NSS programs must do").
The CertDB is created and manipulated by the command line utilities
certutil and modutil. Both of these programs are part of the nss-tools
RPM. Documentation for these tools can be found here:
http://www.mozilla.org/projects/security/pki/nss/tools
Here is an example of creating a CertDB and populating it. In the
example the CertDB will be created under the directory "./pki", the CA
will be called "myca", the database password will be "myca", and the
server's hostname will be "myhost.example.com".
1. Create the database::
certutil -N -d ./pki
This creates a new database under the directory ./pki
2. Create a root CA certificate::
certutil -d ./pki -S -s "CN=myca" -n myca -x -t "CTu,C,C" -m 1
This creates an individual certificate and adds it to the
certificate database with a subject of "CN=myca", a nickname of
"myca", trust flags indicating for SSL indicating it can issue
server certificates (C), can issue client certificates (T), and the
certificate can be used for authentication and signing (u). For
email and object signing it's trusted to create server
certificates. The certificate serial number is set to 1.
3. Create a server certificate and sign it. Our example server will
use this::
certutil -d pki -S -c myca -s "CN=myhost.example.com" -n myhost -t "u,u,u" -m 2
This creates an individual certificate issued by the CA "myca" and
adds it to the certificate database with a subject of
"CN=myhost.example.com", a nickname of "myhost". The certificate
serial number is set to 2.
4. Import public root CA's::
modutil -add ca_certs -libfile /usr/lib/libnssckbi.so -dbdir ./pki
This is necessary to verify certificates presented by a SSL server a
NSS client might connect to. When verifying a certificate the NSS
library will "walk the certificate chain" back to a root CA which
must be trusted. This command imports the well known root CA's as a
PKCS #11 module.
===============================
Things All NSS programs must do
===============================
- Import the NSS/NSPR modules::
from nss.error import NSPRError
import nss.io as io
import nss.nss as nss
import nss.ssl as ssl
In the interest of code brevity we drop the leading "nss." from the
module namespace.
- Initialize NSS and indicate the certficate database (CertDB)::
certdir = './pki'
ssl.nssinit(certdir)
- If you are implementing an SSL server call config_secure_server()
(see ssl_example.py)::
sock = ssl.SSLSocket(net_addr.family)
sock.config_secure_server(server_cert, priv_key, server_cert_kea)
**WARNING** you must call config_secure_server() for SSL servers, if
you do not call it the most likely result will be the NSS library
will segfault (not pretty).
========
Examples
========
There are example programs in under "examples" in the documentation
directory. On Fedora/RHEL/CentOS systems this will be
/usr/share/doc/python-nss.
The ssl_example.py sample implements both a client and server in one
script. You tell it whether to run as a client (-C) or a server (-S)
when you invoke it. The sample shows many of the NSS/NSPR calls and
fully implements basic non-SSL client/server using NSPR, SSL
client/server using NSS, certificate validation, CertDB operations,
and client authentication using certificates.
To get a list of command line options::
ssl_example.py --help
Using the above example certificate database server can be run like
this::
ssl_example.py -S -c ./pki -n myhost
The client can be run like this::
ssl_example.py -C -c ./pki
======
Issues
======
- The current partitioning of the NSS and NSPR API's into Python
modules (i.e. the Python namespaces and their symbols) is a first
cut and may not be ideal. One should be prepared for name changes as
the binding matures.
- NSPR vs. Python
An original design goal of NSS was to be portable, however NSS
required access to many system level functions which can vary
widely between platforms and OS's. Therefore NSPR was written to
encapsulate system services such as IO, sockets, threads, timers,
etc. into a common API to insulate NSS from the underlying
platform.
In many respects Python and its collection of packages and modules
provides the same type of platform independence for applications
and libraries and provides it's own implementation of IO, sockets,
threads, timers, etc.
Unfortunately NSPR's and Python's run time abstractions are not
the same nor can either be configured to use a different
underlying abstraction layer.
Currently the NSS binding utilizes *only* the NSPR abstraction
layer. One consequence of this is it is not possible to create a
Python socket and use it as the foundation for any NSS functions
expecting a socket, or visa versa.
You **must** use the nss.io module to create and manipulate a
socket used by NSS. You cannot pass this socket to any Python
library function expecting a socket. The two are not compatible.
Here are some reasons for this incompatibility, perhaps in the
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.
- NSPR would like to hide the underlying platform socket (in the
NSPR code this is called "osfd"). There are NSPR API's which
will operate on osfd's
- One can base a NSPR socket on an existing osfd via:
- PR_ImportFile()
- PR_ImportPipe()
- PR_ImportTCPSocket()
- PR_ImportUDPSocket()
- One can obtain the osfd in use by NSPR, either when the
osfd was imported or because NSPR created the osfd itself via:
- PR_FileDesc2NativeHandle();
But note this function is not meant to be public in the NSPR
API and is documented as being deprecated and carries an
explicit warning against it's use.
Once NSPR gets a hold of an osfd it manipulates it in a manner
as if it were the only owner of the osfd. Other native code
(e.g. the CPython socket code) which operates on the fd may run
afoul of NSPR belief it is the only code in the system operating
on the fd. For example in CPython the non-blocking flag is
directly set on the fd and non-blocking behavior is implemented
by the OS. However, NSPR manages non-blocking behavior
internally to the NSPR library eschewing direct OS support for
non-blocking. Thus CPython and NSPR are in direct conflict over
when and how non-blocking is set on an fd. Examples of this
problem can be seen in the Python socket.makefile() operation
which takes the fd belonging to a system socket, dups it, and
calls fdopen() on the dup'ed fd to return a FILE stream (all
Python file IO is based on file objects utilizing a FILE
stream). However, the dup'ed fd does not share the same
non-blocking flag, NSPR explicitly forces the flag off, Python
wants to directly manipulate it. Dup'ed fd's share their flags
thus if Python operates on the dup'ed fd returned by NSPR it's
going to confuse NSPR. Likewise if one sets non-blocking via
NSPR then Python won't honor the flag because Python is
expecting the flag to be set on the fd, not in some other
location (e.g. internal to NSPR).
- Python's socket implementation is a very thin layer over the
Berkely socket API. There is very little abstraction, thus
Python and Python program expect to manipulate sockets directly
via their fd's.
- The error and exception model for Python sockets and SSL is an
almost direct one-to-one mapping of the Posix and OpenSSL
errors. But NSS uses NSPR errors, thus Python code which has
exception handlers for sockets and SSL are expecting a complete
different set of exceptions.
- Python's SSL implementation is a very thin layer over the
OpenSSL API, there is little abstraction. Thus there is a
sizeable body of Python code which expects the OpenSSL model for
IO ready and has exception handlers based on OpenSSL.
===
FAQ
===
To be added
- nss.error: This module defines the NSPR errors and provides functions to
manipulate them.
- nss.io: This module implements the NSPR IO functions
- nss.nss: This module implements the NSS functions
- nss.ssl: This module implements the SSL functionality in NSS
|