New submission from Guido van Rossum:

Bill Janssen's patch for server-side ssl support (and certificate
support). This still needs style cleanup, but is leak-free and passes tests.

----------
assignee: gvanrossum
components: Extension Modules
files: ssl-svr.diff
keywords: patch
messages: 55281
nosy: gvanrossum
severity: normal
status: open
title: server-side ssl support
type: behavior
versions: Python 2.6

__________________________________
Tracker <[EMAIL PROTECTED]>
<http://bugs.python.org/issue1018>
__________________________________
Index: Doc/library/socket.rst
===================================================================
--- Doc/library/socket.rst	(revision 57412)
+++ Doc/library/socket.rst	(working copy)
@@ -776,7 +776,15 @@
 
    Returns a string describing the server's certificate. Useful for debugging
    purposes; do not parse the content of this string because its format can't be
-   parsed unambiguously.
+   parsed unambiguously.  And don't *trust* the content of this string, because
+   certificates aren't validated if you use the function :func:`ssl` to create an
+   SSL binding.  If you
+   need to see the content of a peer certificate, you should use the :func:`sslsocket`
+   function in the :mod:`ssl` module to create the SSL object, specifying the parameter `cert_req` as :const:`CERT_REQUIRED`,
+   and passing the name of a file containing a collection of certificates to use to validate the peer
+   certificate as the value of the `ca_certs` parameter.  Then use
+   the :meth:`getpeercert` method on that instance
+   to retrieve the contents of the certificate.
 
 
 .. method:: SSL.issuer()
@@ -785,7 +793,6 @@
    debugging purposes; do not parse the content of this string because its format
    can't be parsed unambiguously.
 
-
 .. _socket-example:
 
 Example
Index: Lib/socket.py
===================================================================
--- Lib/socket.py	(revision 57412)
+++ Lib/socket.py	(working copy)
@@ -49,10 +49,17 @@
 _have_ssl = False
 try:
     import _ssl
-    from _ssl import *
-    _have_ssl = True
 except ImportError:
     pass
+else:
+    def ssl (sock, keyfile=None, certfile=None):
+        import ssl as realssl
+        return realssl.sslwrap_simple(sock, keyfile, certfile)
+    sslerror = _ssl.sslerror
+    from _ssl import SSLType, SSL_ERROR_ZERO_RETURN, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, \
+                      SSL_ERROR_WANT_X509_LOOKUP, SSL_ERROR_SYSCALL, SSL_ERROR_SSL, SSL_ERROR_WANT_CONNECT, \
+                      SSL_ERROR_EOF, SSL_ERROR_INVALID_ERROR_CODE, RAND_add, RAND_egd, RAND_status
+    _have_ssl = True
 
 import os, sys
 
@@ -63,17 +70,10 @@
 
 __all__ = ["getfqdn"]
 __all__.extend(os._get_exports_list(_socket))
-if _have_ssl:
-    __all__.extend(os._get_exports_list(_ssl))
 
 _realsocket = socket
-if _have_ssl:
-    _realssl = ssl
-    def ssl(sock, keyfile=None, certfile=None):
-        if hasattr(sock, "_sock"):
-            sock = sock._sock
-        return _realssl(sock, keyfile, certfile)
 
+
 # WSA error codes
 if sys.platform.lower().startswith("win"):
     errorTab = {}
Index: Modules/_ssl.c
===================================================================
--- Modules/_ssl.c	(revision 57412)
+++ Modules/_ssl.c	(working copy)
@@ -5,9 +5,12 @@
    This module is imported by socket.py. It should *not* be used
    directly.
 
+   TODO:  look up C indent standard for Python and teach Emacs to do it
+
 */
 
 #include "Python.h"
+
 enum py_ssl_error {
 	/* these mirror ssl.h */
 	PY_SSL_ERROR_NONE,                 
@@ -23,6 +26,24 @@
 	PY_SSL_ERROR_INVALID_ERROR_CODE
 };
 
+enum py_ssl_server_or_client {
+  PY_SSL_CLIENT,
+  PY_SSL_SERVER
+};
+
+enum py_ssl_cert_requirements {
+  PY_SSL_CERT_NONE,
+  PY_SSL_CERT_OPTIONAL,
+  PY_SSL_CERT_REQUIRED
+};
+
+enum py_ssl_version {
+  PY_SSL_VERSION_SSL2,
+  PY_SSL_VERSION_SSL3,
+  PY_SSL_VERSION_SSL23,
+  PY_SSL_VERSION_TLS1,
+};
+
 /* Include symbols from _socket module */
 #include "socketmodule.h"
 
@@ -60,7 +81,7 @@
 	PySocketSockObject *Socket;	/* Socket on which we're layered */
 	SSL_CTX* 	ctx;
 	SSL*     	ssl;
-	X509*    	server_cert;
+	X509*    	peer_cert;
 	char    	server[X509_NAME_MAXLEN];
 	char		issuer[X509_NAME_MAXLEN];
 
@@ -71,7 +92,9 @@
 static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args);
 static int check_socket_and_wait_for_timeout(PySocketSockObject *s, 
 					     int writing);
+static PyObject *PySSL_peercert(PySSLObject *self);
 
+
 #define PySSLObject_Check(v)	(Py_Type(v) == &PySSL_Type)
 
 typedef enum {
@@ -83,15 +106,21 @@
 	SOCKET_OPERATION_OK
 } timeout_state;
 
+/* Wrap error strings with filename and line # */
+#define STRINGIFY1(x) #x
+#define STRINGIFY2(x) STRINGIFY1(x)
+#define ERRSTR1(x,y,z) (x ":" y ": " z)
+#define ERRSTR(x) ERRSTR1("_ssl.c", STRINGIFY2(__LINE__), x)
+
 /* XXX It might be helpful to augment the error message generated
    below with the name of the SSL function that generated the error.
    I expect it's obvious most of the time.
 */
 
 static PyObject *
-PySSL_SetError(PySSLObject *obj, int ret)
+PySSL_SetError(PySSLObject *obj, int ret, char *filename, int lineno)
 {
-	PyObject *v, *n, *s;
+	PyObject *v;
 	char *errstr;
 	int err;
 	enum py_ssl_error p;
@@ -158,29 +187,23 @@
 		p = PY_SSL_ERROR_INVALID_ERROR_CODE;
 		errstr = "Invalid error code";
 	}
-	n = PyInt_FromLong((long) p);
-	if (n == NULL)
-		return NULL;
-	v = PyTuple_New(2);
-	if (v == NULL) {
-		Py_DECREF(n);
-		return NULL;
-	}
 
-	s = PyString_FromString(errstr);
-	if (s == NULL) {
+        char buf[2048];
+        PyOS_snprintf(buf, sizeof(buf), "_ssl.c:%d: %s", lineno, errstr);
+	v = Py_BuildValue("(is)", p, buf);
+	if (v != NULL) {
+		PyErr_SetObject(PySSLErrorObject, v);
 		Py_DECREF(v);
-		Py_DECREF(n);
 	}
-	PyTuple_SET_ITEM(v, 0, n);
-	PyTuple_SET_ITEM(v, 1, s);
-	PyErr_SetObject(PySSLErrorObject, v);
-	Py_DECREF(v);
 	return NULL;
 }
 
 static PySSLObject *
-newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file)
+newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file,
+               enum py_ssl_server_or_client socket_type,
+               enum py_ssl_cert_requirements certreq,
+               enum py_ssl_version proto_version,
+               char *cacerts_file)
 {
 	PySSLObject *self;
 	char *errstr = NULL;
@@ -193,31 +216,59 @@
 		return NULL;
 	memset(self->server, '\0', sizeof(char) * X509_NAME_MAXLEN);
 	memset(self->issuer, '\0', sizeof(char) * X509_NAME_MAXLEN);
-	self->server_cert = NULL;
+	self->peer_cert = NULL;
 	self->ssl = NULL;
 	self->ctx = NULL;
 	self->Socket = NULL;
 
 	if ((key_file && !cert_file) || (!key_file && cert_file)) {
-		errstr = "Both the key & certificate files must be specified";
+		errstr = ERRSTR("Both the key & certificate files must be specified");
 		goto fail;
 	}
 
+        if ((socket_type == PY_SSL_SERVER) &&
+            ((key_file == NULL) || (cert_file == NULL))) {
+		errstr = ERRSTR("Both the key & certificate files must be specified for server-side operation");
+		goto fail;
+	}
+
 	Py_BEGIN_ALLOW_THREADS
+        if (proto_version == PY_SSL_VERSION_TLS1)
+          self->ctx = SSL_CTX_new(TLSv1_method()); /* Set up context */
+        else if (proto_version == PY_SSL_VERSION_SSL3)
+          self->ctx = SSL_CTX_new(SSLv3_method()); /* Set up context */
+        else if (proto_version == PY_SSL_VERSION_SSL2)
+          self->ctx = SSL_CTX_new(SSLv2_method()); /* Set up context */
+        else
 	self->ctx = SSL_CTX_new(SSLv23_method()); /* Set up context */
 	Py_END_ALLOW_THREADS
+
 	if (self->ctx == NULL) {
-		errstr = "SSL_CTX_new error";
+		errstr = ERRSTR("Invalid SSL protocol variant specified.");
 		goto fail;
 	}
 
+        if (certreq != PY_SSL_CERT_NONE) {
+                if (cacerts_file == NULL) {
+                        errstr = ERRSTR("No root certificates specified for verification of other-side certificates.");
+                        goto fail;
+                } else {
+                      Py_BEGIN_ALLOW_THREADS
+                      ret = SSL_CTX_load_verify_locations(self->ctx, cacerts_file, NULL);
+                      Py_END_ALLOW_THREADS
+                      if (ret < 1) {
+                          errstr = ERRSTR("SSL_CTX_load_verify_locations");
+                          goto fail;
+                      }
+                }
+        }
 	if (key_file) {
 		Py_BEGIN_ALLOW_THREADS
 		ret = SSL_CTX_use_PrivateKey_file(self->ctx, key_file,
 						SSL_FILETYPE_PEM);
 		Py_END_ALLOW_THREADS
 		if (ret < 1) {
-			errstr = "SSL_CTX_use_PrivateKey_file error";
+			errstr = ERRSTR("SSL_CTX_use_PrivateKey_file error");
 			goto fail;
 		}
 
@@ -225,16 +276,21 @@
 		ret = SSL_CTX_use_certificate_chain_file(self->ctx,
 						       cert_file);
 		Py_END_ALLOW_THREADS
-		SSL_CTX_set_options(self->ctx, SSL_OP_ALL); /* ssl compatibility */
 		if (ret < 1) {
-			errstr = "SSL_CTX_use_certificate_chain_file error";
+			errstr = ERRSTR("SSL_CTX_use_certificate_chain_file error") ;
 			goto fail;
 		}
+		SSL_CTX_set_options(self->ctx, SSL_OP_ALL); /* ssl compatibility */
 	}
 
+        int verification_mode = SSL_VERIFY_NONE;
+        if (certreq == PY_SSL_CERT_OPTIONAL)
+          verification_mode = SSL_VERIFY_PEER;
+        else if (certreq == PY_SSL_CERT_REQUIRED)
+          verification_mode = (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
+	SSL_CTX_set_verify(self->ctx, verification_mode, NULL); /* set verify lvl */
+
 	Py_BEGIN_ALLOW_THREADS
-	SSL_CTX_set_verify(self->ctx,
-			   SSL_VERIFY_NONE, NULL); /* set verify lvl */
 	self->ssl = SSL_new(self->ctx); /* New ssl struct */
 	Py_END_ALLOW_THREADS
 	SSL_set_fd(self->ssl, Sock->sock_fd);	/* Set the socket for SSL */
@@ -249,7 +305,10 @@
 	}
 
 	Py_BEGIN_ALLOW_THREADS
-	SSL_set_connect_state(self->ssl);
+           if (socket_type == PY_SSL_CLIENT)
+              SSL_set_connect_state(self->ssl);
+           else
+             SSL_set_accept_state(self->ssl);
 	Py_END_ALLOW_THREADS
 
 	/* Actually negotiate SSL connection */
@@ -257,7 +316,10 @@
 	sockstate = 0;
 	do {
 		Py_BEGIN_ALLOW_THREADS
-		ret = SSL_connect(self->ssl);
+                 if (socket_type == PY_SSL_CLIENT)
+                    ret = SSL_connect(self->ssl);
+                 else
+                   ret = SSL_accept(self->ssl);
 		err = SSL_get_error(self->ssl, ret);
 		Py_END_ALLOW_THREADS
 		if(PyErr_CheckSignals()) {
@@ -271,29 +333,29 @@
 			sockstate = SOCKET_OPERATION_OK;
 		}
 	        if (sockstate == SOCKET_HAS_TIMED_OUT) {
-			PyErr_SetString(PySSLErrorObject, "The connect operation timed out");
+			PyErr_SetString(PySSLErrorObject, ERRSTR("The connect operation timed out"));
 			goto fail;
 		} else if (sockstate == SOCKET_HAS_BEEN_CLOSED) {
-			PyErr_SetString(PySSLErrorObject, "Underlying socket has been closed.");
+			PyErr_SetString(PySSLErrorObject, ERRSTR("Underlying socket has been closed."));
 			goto fail;
 		} else if (sockstate == SOCKET_TOO_LARGE_FOR_SELECT) {
-			PyErr_SetString(PySSLErrorObject, "Underlying socket too large for select().");
+			PyErr_SetString(PySSLErrorObject, ERRSTR("Underlying socket too large for select()."));
 			goto fail;
 		} else if (sockstate == SOCKET_IS_NONBLOCKING) {
 			break;
 		}
 	} while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE);
-	if (ret <= 0) {
-		PySSL_SetError(self, ret);
+	if (ret < 1) {
+		PySSL_SetError(self, ret, __FILE__, __LINE__);
 		goto fail;
 	}
 	self->ssl->debug = 1;
 
 	Py_BEGIN_ALLOW_THREADS
-	if ((self->server_cert = SSL_get_peer_certificate(self->ssl))) {
-		X509_NAME_oneline(X509_get_subject_name(self->server_cert),
+	if ((self->peer_cert = SSL_get_peer_certificate(self->ssl))) {
+		X509_NAME_oneline(X509_get_subject_name(self->peer_cert),
 				  self->server, X509_NAME_MAXLEN);
-		X509_NAME_oneline(X509_get_issuer_name(self->server_cert),
+		X509_NAME_oneline(X509_get_issuer_name(self->peer_cert),
 				  self->issuer, X509_NAME_MAXLEN);
 	}
 	Py_END_ALLOW_THREADS
@@ -310,25 +372,34 @@
 static PyObject *
 PySocket_ssl(PyObject *self, PyObject *args)
 {
-	PySSLObject *rv;
 	PySocketSockObject *Sock;
+        int server_side = 0;
+        int verification_mode = PY_SSL_CERT_NONE;
+        int protocol = PY_SSL_VERSION_SSL23;
 	char *key_file = NULL;
 	char *cert_file = NULL;
+        char *cacerts_file = NULL;
 
-	if (!PyArg_ParseTuple(args, "O!|zz:ssl",
+	if (!PyArg_ParseTuple(args, "O!i|zziiz:sslwrap",
 			      PySocketModule.Sock_Type,
 			      &Sock,
-			      &key_file, &cert_file))
+                              &server_side,
+			      &key_file, &cert_file,
+                              &verification_mode, &protocol,
+                              &cacerts_file))
 		return NULL;
 
-	rv = newPySSLObject(Sock, key_file, cert_file);
-	if (rv == NULL)
-		return NULL;
-	return (PyObject *)rv;
+        /*
+        fprintf(stderr, "server_side is %d, keyfile %p, certfile %p, verify_mode %d, protocol %d, certs %p\n",
+                server_side, key_file, cert_file, verification_mode, protocol, cacerts_file);
+         */
+
+	return (PyObject *) newPySSLObject(Sock, key_file, cert_file, server_side,
+                                           verification_mode, protocol, cacerts_file);
 }
 
 PyDoc_STRVAR(ssl_doc,
-"ssl(socket, [keyfile, certfile]) -> sslobject");
+"sslwrap(socket, server_side, [keyfile, certfile, certs_mode, protocol, cacertsfile]) -> sslobject");
 
 /* SSL object methods */
 
@@ -344,11 +415,148 @@
 	return PyString_FromString(self->issuer);
 }
 
+static PyObject *
+_create_dict_for_X509_NAME (X509_NAME *xname) {
+    PyObject *pd = PyDict_New();
+    int index_counter;
 
+    for (index_counter = 0;  index_counter < X509_NAME_entry_count(xname);  index_counter++) {
+      char namebuf[X509_NAME_MAXLEN];
+      int buflen;
+
+      X509_NAME_ENTRY *entry = X509_NAME_get_entry(xname, index_counter);
+
+      ASN1_OBJECT *name = X509_NAME_ENTRY_get_object(entry);
+      buflen = OBJ_obj2txt(namebuf, sizeof(namebuf), name, 0);
+      if (buflen < 0)
+        goto fail0;
+      PyObject *name_obj = PyString_FromStringAndSize(namebuf, buflen);
+      if (name_obj == NULL)
+        goto fail0;
+
+      ASN1_STRING *value = X509_NAME_ENTRY_get_data(entry);
+      unsigned char *valuebuf = NULL;
+      buflen = ASN1_STRING_to_UTF8(&valuebuf, value);
+      if (buflen < 0) {
+          Py_DECREF(name_obj);
+          goto fail0;
+      }
+      PyObject *value_obj = PyUnicode_DecodeUTF8((char *) valuebuf, buflen, "strict");
+      OPENSSL_free(valuebuf);
+      if (value_obj == NULL) {
+          Py_DECREF(name_obj);
+          goto fail0;
+      }
+      if (PyDict_SetItem(pd, name_obj, value_obj) < 0) {
+          Py_DECREF(name_obj);
+          Py_DECREF(value_obj);
+          goto fail0;
+      }
+      Py_DECREF(name_obj);
+      Py_DECREF(value_obj);
+    }
+    return pd;
+
+  fail0:
+    Py_XDECREF(pd);
+    return NULL;
+}
+
+static PyObject *
+PySSL_peercert(PySSLObject *self) {
+
+  PyObject *retval = NULL;
+  BIO *biobuf = NULL;
+
+  if (!self->peer_cert)
+	  Py_RETURN_NONE;
+
+  {
+      retval = PyDict_New();
+      if (retval == NULL)
+	      return NULL;
+
+      int verification = SSL_CTX_get_verify_mode(self->ctx);
+      if ((verification & SSL_VERIFY_PEER) == 0)
+	      return retval;
+      {
+          PyObject *peer = _create_dict_for_X509_NAME(X509_get_subject_name(self->peer_cert));
+          if (peer == NULL)
+            goto fail0;
+          if (PyDict_SetItemString(retval, (const char *) "subject", peer) < 0) {
+              Py_DECREF(peer);
+              goto fail0;
+          }
+          Py_DECREF(peer);
+          
+          PyObject *issuer = _create_dict_for_X509_NAME(X509_get_issuer_name(self->peer_cert));
+          if (issuer == NULL)
+            goto fail0;
+          if (PyDict_SetItemString(retval, (const char *) "issuer", issuer) < 0) {
+              Py_DECREF(issuer);
+              goto fail0;
+          }
+          Py_DECREF(issuer);
+
+          PyObject *version = PyInt_FromLong(X509_get_version(self->peer_cert));
+          if (PyDict_SetItemString(retval, "version", version) < 0) {
+              Py_DECREF(version);
+              goto fail0;
+          }
+          Py_DECREF(version);
+
+          char buf[2048];
+          int len;
+
+          /* get a memory buffer */
+          biobuf = BIO_new(BIO_s_mem());
+
+          ASN1_TIME *notBefore = X509_get_notBefore(self->peer_cert);
+          ASN1_TIME_print(biobuf, notBefore);
+          len = BIO_gets(biobuf, buf, sizeof(buf)-1);
+          PyObject *pnotBefore = PyString_FromStringAndSize(buf, len);
+          if (pnotBefore == NULL)
+            goto fail1;
+          if (PyDict_SetItemString(retval, "notBefore", pnotBefore) < 0) {
+              Py_DECREF(pnotBefore);
+              goto fail1;
+          }
+          Py_DECREF(pnotBefore);
+      
+          BIO_reset(biobuf);
+          ASN1_TIME *notAfter = X509_get_notAfter(self->peer_cert);
+          ASN1_TIME_print(biobuf, notAfter);
+          len = BIO_gets(biobuf, buf, sizeof(buf)-1);
+	  BIO_free(biobuf);
+          PyObject *pnotAfter = PyString_FromStringAndSize(buf, len);
+          if (pnotAfter == NULL)
+            goto fail0;
+          if (PyDict_SetItemString(retval, "notAfter", pnotAfter) < 0) {
+              Py_DECREF(pnotAfter);
+              goto fail0;
+          }
+          Py_DECREF(pnotAfter);
+
+      }
+
+  }
+  return retval;
+
+ fail1:
+  if (biobuf != NULL) {
+      BIO_free(biobuf);
+  }
+ fail0:
+  if (retval != NULL) {
+      Py_DECREF(retval);
+  }
+  return NULL;
+}
+
 static void PySSL_dealloc(PySSLObject *self)
 {
-	if (self->server_cert)	/* Possible not to have one? */
-		X509_free (self->server_cert);
+	if (self->peer_cert)	/* Possible not to have one? */
+		X509_free (self->peer_cert);
 	if (self->ssl)
 	    SSL_free(self->ssl);
 	if (self->ctx)
@@ -476,7 +684,7 @@
 	if (len > 0)
 		return PyInt_FromLong(len);
 	else
-		return PySSL_SetError(self, len);
+		return PySSL_SetError(self, len, __FILE__, __LINE__);
 }
 
 PyDoc_STRVAR(PySSL_SSLwrite_doc,
@@ -512,7 +720,18 @@
 			return NULL;
 		} else if (sockstate == SOCKET_TOO_LARGE_FOR_SELECT) {
 			PyErr_SetString(PySSLErrorObject, "Underlying socket too large for select().");
-			return NULL;
+                   Py_DECREF(buf);
+                   return NULL;
+ 		} else if (sockstate == SOCKET_HAS_BEEN_CLOSED) {
+                   if (SSL_get_shutdown(self->ssl) != SSL_RECEIVED_SHUTDOWN) {
+                     Py_DECREF(buf);
+                     PyErr_SetString(PySSLErrorObject, "Socket closed without SSL shutdown handshake");
+                     return NULL;
+                   } else {
+                       /* should contain a zero-length string */
+                       _PyString_Resize(&buf, 0);
+                       return buf;
+                   }
 		}
 	}
 	do {
@@ -529,6 +748,10 @@
 			sockstate = check_socket_and_wait_for_timeout(self->Socket, 0);
 		} else if (err == SSL_ERROR_WANT_WRITE) {
 			sockstate = check_socket_and_wait_for_timeout(self->Socket, 1);
+                } else if ((err == SSL_ERROR_ZERO_RETURN) &&
+                           (SSL_get_shutdown(self->ssl) == SSL_RECEIVED_SHUTDOWN)) {
+                      _PyString_Resize(&buf, 0);
+                      return buf;
 		} else {
 			sockstate = SOCKET_OPERATION_OK;
 		}
@@ -542,7 +765,7 @@
 	} while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE);
  	if (count <= 0) {
 		Py_DECREF(buf);
-		return PySSL_SetError(self, count);
+		return PySSL_SetError(self, count, __FILE__, __LINE__);
 	}
 	if (count != len)
 		_PyString_Resize(&buf, count);
@@ -554,6 +777,38 @@
 \n\
 Read up to len bytes from the SSL socket.");
 
+static PyObject *PySSL_SSLshutdown(PySSLObject *self, PyObject *args)
+{
+	int err;
+
+	/* Guard against closed socket */
+	if (self->Socket->sock_fd < 0) {
+          PyErr_SetString(PySSLErrorObject, "Underlying socket has been closed.");
+          return NULL;
+        }
+
+        Py_BEGIN_ALLOW_THREADS
+        err = SSL_shutdown(self->ssl);
+        if (err == 0) {
+            /* we need to call it again to finish the shutdown */
+            err = SSL_shutdown(self->ssl);
+        }
+        Py_END_ALLOW_THREADS
+
+        if (err < 0)
+          return PySSL_SetError(self, err, __FILE__, __LINE__);
+        else {
+          Py_INCREF(self->Socket);
+          return (PyObject *) (self->Socket);
+        }
+}
+
+PyDoc_STRVAR(PySSL_SSLshutdown_doc,
+"shutdown(s) -> socket\n\
+\n\
+Does the SSL shutdown handshake with the remote end, and returns\n\
+the underlying socket object.");
+
 static PyMethodDef PySSLMethods[] = {
 	{"write", (PyCFunction)PySSL_SSLwrite, METH_VARARGS,
 	          PySSL_SSLwrite_doc},
@@ -561,6 +816,8 @@
 	          PySSL_SSLread_doc},
 	{"server", (PyCFunction)PySSL_server, METH_NOARGS},
 	{"issuer", (PyCFunction)PySSL_issuer, METH_NOARGS},
+	{"peer_certificate", (PyCFunction)PySSL_peercert, METH_NOARGS},
+	{"shutdown", (PyCFunction)PySSL_SSLshutdown, METH_NOARGS, PySSL_SSLshutdown_doc},
 	{NULL, NULL}
 };
 
@@ -654,7 +911,7 @@
 /* List of functions exported by this module. */
 
 static PyMethodDef PySSL_methods[] = {
-	{"ssl",			PySocket_ssl,
+	{"sslwrap",		PySocket_ssl,
 	 METH_VARARGS, ssl_doc},
 #ifdef HAVE_OPENSSL_RAND
 	{"RAND_add",            PySSL_RAND_add, METH_VARARGS, 
@@ -699,6 +956,7 @@
 	if (PySSLErrorObject == NULL)
 		return;
 	PyDict_SetItemString(d, "sslerror", PySSLErrorObject);
+
 	if (PyDict_SetItemString(d, "SSLType",
 				 (PyObject *)&PySSL_Type) != 0)
 		return;
@@ -721,5 +979,21 @@
 				PY_SSL_ERROR_EOF);
 	PyModule_AddIntConstant(m, "SSL_ERROR_INVALID_ERROR_CODE",
 				PY_SSL_ERROR_INVALID_ERROR_CODE);
+        /* cert requirements */
+	PyModule_AddIntConstant(m, "CERT_NONE",
+                                PY_SSL_CERT_NONE);
+	PyModule_AddIntConstant(m, "CERT_OPTIONAL",
+                                PY_SSL_CERT_OPTIONAL);
+	PyModule_AddIntConstant(m, "CERT_REQUIRED",
+                                PY_SSL_CERT_REQUIRED);
 
+        /* protocol versions */
+	PyModule_AddIntConstant(m, "PROTOCOL_SSLv2",
+                                PY_SSL_VERSION_SSL2);
+	PyModule_AddIntConstant(m, "PROTOCOL_SSLv3",
+                                PY_SSL_VERSION_SSL3);
+	PyModule_AddIntConstant(m, "PROTOCOL_SSLv23",
+                                PY_SSL_VERSION_SSL23);
+	PyModule_AddIntConstant(m, "PROTOCOL_TLSv1",
+                                PY_SSL_VERSION_TLS1);
 }
_______________________________________________
Python-bugs-list mailing list 
Unsubscribe: 
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to