anope

- supernets anope source code & configuration
git clone git://git.acid.vegas/anope.git
Log | Files | Refs | Archive | README

m_ssl_gnutls.cpp (16779B)

      1 /*
      2  *
      3  * (C) 2014 Attila Molnar <attilamolnar@hush.com>
      4  * (C) 2014-2022 Anope Team
      5  * Contact us at team@anope.org
      6  *
      7  * Please read COPYING and README for further details.
      8  */
      9 
     10 /* RequiredLibraries: gnutls */
     11 /* RequiredWindowsLibraries: libgnutls-30 */
     12 
     13 #include "module.h"
     14 #include "modules/ssl.h"
     15 
     16 #include <errno.h>
     17 #include <gnutls/gnutls.h>
     18 #include <gnutls/x509.h>
     19 
     20 class GnuTLSModule;
     21 static GnuTLSModule *me;
     22 
     23 namespace GnuTLS { class X509CertCredentials; }
     24 
     25 class MySSLService : public SSLService
     26 {
     27  public:
     28 	MySSLService(Module *o, const Anope::string &n);
     29 
     30 	/** Initialize a socket to use SSL
     31 	 * @param s The socket
     32 	 */
     33 	void Init(Socket *s) anope_override;
     34 };
     35 
     36 class SSLSocketIO : public SocketIO
     37 {
     38  public:
     39 	gnutls_session_t sess;
     40 	GnuTLS::X509CertCredentials* mycreds;
     41 
     42 	/** Constructor
     43 	 */
     44 	SSLSocketIO();
     45 
     46 	/** Really receive something from the buffer
     47 	 * @param s The socket
     48 	 * @param buf The buf to read to
     49 	 * @param sz How much to read
     50 	 * @return Number of bytes received
     51 	 */
     52 	int Recv(Socket *s, char *buf, size_t sz) anope_override;
     53 
     54 	/** Write something to the socket
     55 	 * @param s The socket
     56 	 * @param buf The data to write
     57 	 * @param size The length of the data
     58 	 */
     59 	int Send(Socket *s, const char *buf, size_t sz) anope_override;
     60 
     61 	/** Accept a connection from a socket
     62 	 * @param s The socket
     63 	 * @return The new socket
     64 	 */
     65 	ClientSocket *Accept(ListenSocket *s) anope_override;
     66 
     67 	/** Finished accepting a connection from a socket
     68 	 * @param s The socket
     69 	 * @return SF_ACCEPTED if accepted, SF_ACCEPTING if still in process, SF_DEAD on error
     70 	 */
     71 	SocketFlag FinishAccept(ClientSocket *cs) anope_override;
     72 
     73 	/** Connect the socket
     74 	 * @param s THe socket
     75 	 * @param target IP to connect to
     76 	 * @param port to connect to
     77 	 */
     78 	void Connect(ConnectionSocket *s, const Anope::string &target, int port) anope_override;
     79 
     80 	/** Called to potentially finish a pending connection
     81 	 * @param s The socket
     82 	 * @return SF_CONNECTED on success, SF_CONNECTING if still pending, and SF_DEAD on error.
     83 	 */
     84 	SocketFlag FinishConnect(ConnectionSocket *s) anope_override;
     85 
     86 	/** Called when the socket is destructing
     87 	 */
     88 	void Destroy() anope_override;
     89 };
     90 
     91 namespace GnuTLS
     92 {
     93 	class Init
     94 	{
     95 	 public:
     96 		Init() { gnutls_global_init(); }
     97 		~Init() { gnutls_global_deinit(); }
     98 	};
     99 
    100 	/** Used to create a gnutls_datum_t* from an Anope::string
    101 	 */
    102 	class Datum
    103 	{
    104 		gnutls_datum_t datum;
    105 
    106 	 public:
    107 		Datum(const Anope::string &dat)
    108 		{
    109 			datum.data = reinterpret_cast<unsigned char *>(const_cast<char *>(dat.data()));
    110 			datum.size = static_cast<unsigned int>(dat.length());
    111 		}
    112 
    113 		const gnutls_datum_t *get() const { return &datum; }
    114 	};
    115 
    116 	class DHParams
    117 	{
    118 		gnutls_dh_params_t dh_params;
    119 
    120 	 public:
    121 		DHParams() : dh_params(NULL) { }
    122 
    123 		void Import(const Anope::string &dhstr)
    124 		{
    125 			if (dh_params != NULL)
    126 			{
    127 				gnutls_dh_params_deinit(dh_params);
    128 				dh_params = NULL;
    129 			}
    130 
    131 			int ret = gnutls_dh_params_init(&dh_params);
    132 			if (ret < 0)
    133 				throw ConfigException("Unable to initialize DH parameters");
    134 
    135 			ret = gnutls_dh_params_import_pkcs3(dh_params, Datum(dhstr).get(), GNUTLS_X509_FMT_PEM);
    136 			if (ret < 0)
    137 			{
    138 				gnutls_dh_params_deinit(dh_params);
    139 				dh_params = NULL;
    140 				throw ConfigException("Unable to import DH parameters");
    141 			}
    142 		}
    143 
    144 		~DHParams()
    145 		{
    146 			if (dh_params)
    147 				gnutls_dh_params_deinit(dh_params);
    148 		}
    149 
    150 		gnutls_dh_params_t get() const { return dh_params; }
    151 	};
    152 
    153 	class X509Key
    154 	{
    155 		/** Ensure that the key is deinited in case the constructor of X509Key throws
    156 		 */
    157 		class RAIIKey
    158 		{
    159 		 public:
    160 			gnutls_x509_privkey_t key;
    161 
    162 			RAIIKey()
    163 			{
    164 				int ret = gnutls_x509_privkey_init(&key);
    165 				if (ret < 0)
    166 					throw ConfigException("gnutls_x509_privkey_init() failed");
    167 			}
    168 
    169 			~RAIIKey()
    170 			{
    171 				gnutls_x509_privkey_deinit(key);
    172 			}
    173 		} key;
    174 
    175 	 public:
    176 		/** Import */
    177 		X509Key(const Anope::string &keystr)
    178 		{
    179 			int ret = gnutls_x509_privkey_import(key.key, Datum(keystr).get(), GNUTLS_X509_FMT_PEM);
    180 			if (ret < 0)
    181 				throw ConfigException("Error loading private key: " + Anope::string(gnutls_strerror(ret)));
    182 		}
    183 
    184 		gnutls_x509_privkey_t& get() { return key.key; }
    185 	};
    186 
    187 	class X509CertList
    188 	{
    189 		std::vector<gnutls_x509_crt_t> certs;
    190 
    191 	 public:
    192 		/** Import */
    193 		X509CertList(const Anope::string &certstr)
    194 		{
    195 			unsigned int certcount = 3;
    196 			certs.resize(certcount);
    197 			Datum datum(certstr);
    198 
    199 			int ret = gnutls_x509_crt_list_import(raw(), &certcount, datum.get(), GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
    200 			if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER)
    201 			{
    202 				// the buffer wasn't big enough to hold all certs but gnutls changed certcount to the number of available certs,
    203 				// try again with a bigger buffer
    204 				certs.resize(certcount);
    205 				ret = gnutls_x509_crt_list_import(raw(), &certcount, datum.get(), GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
    206 			}
    207 
    208 			if (ret < 0)
    209 				throw ConfigException("Unable to load certificates" + Anope::string(gnutls_strerror(ret)));
    210 
    211 			// Resize the vector to the actual number of certs because we rely on its size being correct
    212 			// when deallocating the certs
    213 			certs.resize(certcount);
    214 		}
    215 
    216 		~X509CertList()
    217 		{
    218 			for (std::vector<gnutls_x509_crt_t>::iterator i = certs.begin(); i != certs.end(); ++i)
    219 				gnutls_x509_crt_deinit(*i);
    220 		}
    221 
    222 		gnutls_x509_crt_t* raw() { return &certs[0]; }
    223 		unsigned int size() const { return certs.size(); }
    224 	};
    225 
    226 	class X509CertCredentials
    227 	{
    228 		unsigned int refcount;
    229 		gnutls_certificate_credentials_t cred;
    230 		DHParams dh;
    231 
    232 		static Anope::string LoadFile(const Anope::string &filename)
    233 		{
    234 			std::ifstream ifs(filename.c_str());
    235 			const Anope::string ret((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
    236 			return ret;
    237 		}
    238 
    239 		#if (GNUTLS_VERSION_MAJOR < 2 || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR < 12))
    240 		static int cert_callback(gnutls_session_t sess, const gnutls_datum_t* req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t* sign_algos, int sign_algos_length, gnutls_retr_st* st);
    241 		#else
    242 		static int cert_callback(gnutls_session_t sess, const gnutls_datum_t* req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t* sign_algos, int sign_algos_length, gnutls_retr2_st* st);
    243 		#endif
    244 
    245 	 public:
    246 		X509CertList certs;
    247 		X509Key key;
    248 
    249 		X509CertCredentials(const Anope::string &certfile, const Anope::string &keyfile)
    250 			: refcount(0), certs(LoadFile(certfile)), key(LoadFile(keyfile))
    251 		{
    252 			if (gnutls_certificate_allocate_credentials(&cred) < 0)
    253 				throw ConfigException("Cannot allocate certificate credentials");
    254 
    255 			int ret = gnutls_certificate_set_x509_key(cred, certs.raw(), certs.size(), key.get());
    256 			if (ret < 0)
    257 			{
    258 				gnutls_certificate_free_credentials(cred);
    259 				throw ConfigException("Unable to set cert/key pair");
    260 			}
    261 
    262 			#if (GNUTLS_VERSION_MAJOR < 2 || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR < 12))
    263 			gnutls_certificate_client_set_retrieve_function(cred, cert_callback);
    264 			#else
    265 			gnutls_certificate_set_retrieve_function(cred, cert_callback);
    266 			#endif
    267 		}
    268 
    269 		~X509CertCredentials()
    270 		{
    271 			gnutls_certificate_free_credentials(cred);
    272 		}
    273 
    274 		void SetupSession(gnutls_session_t sess)
    275 		{
    276 			gnutls_credentials_set(sess, GNUTLS_CRD_CERTIFICATE, cred);
    277 			gnutls_set_default_priority(sess);
    278 		}
    279 
    280 		void SetDH(const Anope::string &dhfile)
    281 		{
    282 			const Anope::string dhdata = LoadFile(dhfile);
    283 			dh.Import(dhdata);
    284 			gnutls_certificate_set_dh_params(cred, dh.get());
    285 		}
    286 
    287 		bool HasDH() const
    288 		{
    289 			return (dh.get() != NULL);
    290 		}
    291 
    292 		void incrref() { refcount++; }
    293 		void decrref() { if (!--refcount) delete this; }
    294 	};
    295 }
    296 
    297 class GnuTLSModule : public Module
    298 {
    299 	GnuTLS::Init libinit;
    300 
    301  public:
    302 	GnuTLS::X509CertCredentials *cred;
    303 	MySSLService service;
    304 
    305 	GnuTLSModule(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR), cred(NULL), service(this, "ssl")
    306 	{
    307 		me = this;
    308 		this->SetPermanent(true);
    309 	}
    310 
    311 	~GnuTLSModule()
    312 	{
    313 		for (std::map<int, Socket *>::const_iterator it = SocketEngine::Sockets.begin(), it_end = SocketEngine::Sockets.end(); it != it_end;)
    314 		{
    315 			Socket *s = it->second;
    316 			++it;
    317 
    318 			if (dynamic_cast<SSLSocketIO *>(s->io))
    319 				delete s;
    320 		}
    321 
    322 		if (cred)
    323 			cred->decrref();
    324 	}
    325 
    326 	static void CheckFile(const Anope::string &filename)
    327 	{
    328 		if (!Anope::IsFile(filename.c_str()))
    329 		{
    330 			Log() << "File does not exist: " << filename;
    331 			throw ConfigException("Error loading certificate/private key");
    332 		}
    333 	}
    334 
    335 	void OnReload(Configuration::Conf *conf) anope_override
    336 	{
    337 		Configuration::Block *config = conf->GetModule(this);
    338 
    339 		const Anope::string certfile = config->Get<const Anope::string>("cert", "data/anope.crt");
    340 		const Anope::string keyfile = config->Get<const Anope::string>("key", "data/anope.key");
    341 		const Anope::string dhfile = config->Get<const Anope::string>("dh", "data/dhparams.pem");
    342 
    343 		CheckFile(certfile);
    344 		CheckFile(keyfile);
    345 
    346 		GnuTLS::X509CertCredentials *newcred = new GnuTLS::X509CertCredentials(certfile, keyfile);
    347 
    348 		// DH params is not mandatory
    349 		if (Anope::IsFile(dhfile.c_str()))
    350 		{
    351 			try
    352 			{
    353 				newcred->SetDH(dhfile);
    354 			}
    355 			catch (...)
    356 			{
    357 				delete newcred;
    358 				throw;
    359 			}
    360 			Log(LOG_DEBUG) << "m_ssl_gnutls: Successfully loaded DH parameters from " << dhfile;
    361 		}
    362 
    363 		if (cred)
    364 			cred->decrref();
    365 		cred = newcred;
    366 		cred->incrref();
    367 
    368 		Log(LOG_DEBUG) << "m_ssl_gnutls: Successfully loaded certificate " << certfile << " and private key " << keyfile;
    369 	}
    370 
    371 	void OnPreServerConnect() anope_override
    372 	{
    373 		Configuration::Block *config = Config->GetBlock("uplink", Anope::CurrentUplink);
    374 
    375 		if (config->Get<bool>("ssl"))
    376 		{
    377 			this->service.Init(UplinkSock);
    378 		}
    379 	}
    380 };
    381 
    382 MySSLService::MySSLService(Module *o, const Anope::string &n) : SSLService(o, n)
    383 {
    384 }
    385 
    386 void MySSLService::Init(Socket *s)
    387 {
    388 	if (s->io != &NormalSocketIO)
    389 		throw CoreException("Socket initializing SSL twice");
    390 
    391 	s->io = new SSLSocketIO();
    392 }
    393 
    394 int SSLSocketIO::Recv(Socket *s, char *buf, size_t sz)
    395 {
    396 	int ret = gnutls_record_recv(this->sess, buf, sz);
    397 
    398 	if (ret > 0)
    399 		TotalRead += ret;
    400 	else if (ret < 0)
    401 	{
    402 		switch (ret)
    403 		{
    404 			case GNUTLS_E_AGAIN:
    405 			case GNUTLS_E_INTERRUPTED:
    406 				SocketEngine::SetLastError(EAGAIN);
    407 				break;
    408 			default:
    409 				if (s == UplinkSock)
    410 				{
    411 					// Log and fake an errno because this is a fatal error on the uplink socket
    412 					Log() << "SSL error: " << gnutls_strerror(ret);
    413 				}
    414 				SocketEngine::SetLastError(ECONNRESET);
    415 		}
    416 	}
    417 
    418 	return ret;
    419 }
    420 
    421 int SSLSocketIO::Send(Socket *s, const char *buf, size_t sz)
    422 {
    423 	int ret = gnutls_record_send(this->sess, buf, sz);
    424 
    425 	if (ret > 0)
    426 		TotalWritten += ret;
    427 	else
    428 	{
    429 		switch (ret)
    430 		{
    431 			case 0:
    432 			case GNUTLS_E_AGAIN:
    433 			case GNUTLS_E_INTERRUPTED:
    434 				SocketEngine::SetLastError(EAGAIN);
    435 				break;
    436 			default:
    437 				if (s == UplinkSock)
    438 				{
    439 					// Log and fake an errno because this is a fatal error on the uplink socket
    440 					Log() << "SSL error: " << gnutls_strerror(ret);
    441 				}
    442 				SocketEngine::SetLastError(ECONNRESET);
    443 		}
    444 	}
    445 
    446 	return ret;
    447 }
    448 
    449 ClientSocket *SSLSocketIO::Accept(ListenSocket *s)
    450 {
    451 	if (s->io == &NormalSocketIO)
    452 		throw SocketException("Attempting to accept on uninitialized socket with SSL");
    453 
    454 	sockaddrs conaddr;
    455 
    456 	socklen_t size = sizeof(conaddr);
    457 	int newsock = accept(s->GetFD(), &conaddr.sa, &size);
    458 
    459 #ifndef INVALID_SOCKET
    460 	const int INVALID_SOCKET = -1;
    461 #endif
    462 
    463 	if (newsock < 0 || newsock == INVALID_SOCKET)
    464 		throw SocketException("Unable to accept connection: " + Anope::LastError());
    465 
    466 	ClientSocket *newsocket = s->OnAccept(newsock, conaddr);
    467 	me->service.Init(newsocket);
    468 	SSLSocketIO *io = anope_dynamic_static_cast<SSLSocketIO *>(newsocket->io);
    469 
    470 	if (gnutls_init(&io->sess, GNUTLS_SERVER) != GNUTLS_E_SUCCESS)
    471 		throw SocketException("Unable to initialize SSL socket");
    472 
    473 	me->cred->SetupSession(io->sess);
    474 	gnutls_transport_set_ptr(io->sess, reinterpret_cast<gnutls_transport_ptr_t>(newsock));
    475 
    476 	newsocket->flags[SF_ACCEPTING] = true;
    477 	this->FinishAccept(newsocket);
    478 
    479 	return newsocket;
    480 }
    481 
    482 SocketFlag SSLSocketIO::FinishAccept(ClientSocket *cs)
    483 {
    484 	if (cs->io == &NormalSocketIO)
    485 		throw SocketException("Attempting to finish connect uninitialized socket with SSL");
    486 	else if (cs->flags[SF_ACCEPTED])
    487 		return SF_ACCEPTED;
    488 	else if (!cs->flags[SF_ACCEPTING])
    489 		throw SocketException("SSLSocketIO::FinishAccept called for a socket not accepted nor accepting?");
    490 
    491 	SSLSocketIO *io = anope_dynamic_static_cast<SSLSocketIO *>(cs->io);
    492 
    493 	int ret = gnutls_handshake(io->sess);
    494 	if (ret < 0)
    495 	{
    496 		if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
    497 		{
    498 			// gnutls_handshake() wants to read or write again;
    499 			// if gnutls_record_get_direction() returns 0 it wants to read, otherwise it wants to write.
    500 			if (gnutls_record_get_direction(io->sess) == 0)
    501 			{
    502 				SocketEngine::Change(cs, false, SF_WRITABLE);
    503 				SocketEngine::Change(cs, true, SF_READABLE);
    504 			}
    505 			else
    506 			{
    507 				SocketEngine::Change(cs, true, SF_WRITABLE);
    508 				SocketEngine::Change(cs, false, SF_READABLE);
    509 			}
    510 			return SF_ACCEPTING;
    511 		}
    512 		else
    513 		{
    514 			cs->OnError(Anope::string(gnutls_strerror(ret)));
    515 			cs->flags[SF_DEAD] = true;
    516 			cs->flags[SF_ACCEPTING] = false;
    517 			return SF_DEAD;
    518 		}
    519 	}
    520 	else
    521 	{
    522 		cs->flags[SF_ACCEPTED] = true;
    523 		cs->flags[SF_ACCEPTING] = false;
    524 		SocketEngine::Change(cs, false, SF_WRITABLE);
    525 		SocketEngine::Change(cs, true, SF_READABLE);
    526 		cs->OnAccept();
    527 		return SF_ACCEPTED;
    528 	}
    529 }
    530 
    531 void SSLSocketIO::Connect(ConnectionSocket *s, const Anope::string &target, int port)
    532 {
    533 	if (s->io == &NormalSocketIO)
    534 		throw SocketException("Attempting to connect uninitialized socket with SSL");
    535 
    536 	s->flags[SF_CONNECTING] = s->flags[SF_CONNECTED] = false;
    537 
    538 	s->conaddr.pton(s->IsIPv6() ? AF_INET6 : AF_INET, target, port);
    539 	int c = connect(s->GetFD(), &s->conaddr.sa, s->conaddr.size());
    540 	if (c == -1)
    541 	{
    542 		if (Anope::LastErrorCode() != EINPROGRESS)
    543 		{
    544 			s->OnError(Anope::LastError());
    545 			s->flags[SF_DEAD] = true;
    546 			return;
    547 		}
    548 		else
    549 		{
    550 			SocketEngine::Change(s, true, SF_WRITABLE);
    551 			s->flags[SF_CONNECTING] = true;
    552 			return;
    553 		}
    554 	}
    555 	else
    556 	{
    557 		s->flags[SF_CONNECTING] = true;
    558 		this->FinishConnect(s);
    559 	}
    560 }
    561 
    562 SocketFlag SSLSocketIO::FinishConnect(ConnectionSocket *s)
    563 {
    564 	if (s->io == &NormalSocketIO)
    565 		throw SocketException("Attempting to finish connect uninitialized socket with SSL");
    566 	else if (s->flags[SF_CONNECTED])
    567 		return SF_CONNECTED;
    568 	else if (!s->flags[SF_CONNECTING])
    569 		throw SocketException("SSLSocketIO::FinishConnect called for a socket not connected nor connecting?");
    570 
    571 	SSLSocketIO *io = anope_dynamic_static_cast<SSLSocketIO *>(s->io);
    572 
    573 	if (io->sess == NULL)
    574 	{
    575 		if (gnutls_init(&io->sess, GNUTLS_CLIENT) != GNUTLS_E_SUCCESS)
    576 			throw SocketException("Unable to initialize SSL socket");
    577 		me->cred->SetupSession(io->sess);
    578 		gnutls_transport_set_ptr(io->sess, reinterpret_cast<gnutls_transport_ptr_t>(s->GetFD()));
    579 	}
    580 
    581 	int ret = gnutls_handshake(io->sess);
    582 	if (ret < 0)
    583 	{
    584 		if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
    585 		{
    586 			// gnutls_handshake() wants to read or write again;
    587 			// if gnutls_record_get_direction() returns 0 it wants to read, otherwise it wants to write.
    588 			if (gnutls_record_get_direction(io->sess) == 0)
    589 			{
    590 				SocketEngine::Change(s, false, SF_WRITABLE);
    591 				SocketEngine::Change(s, true, SF_READABLE);
    592 			}
    593 			else
    594 			{
    595 				SocketEngine::Change(s, true, SF_WRITABLE);
    596 				SocketEngine::Change(s, false, SF_READABLE);
    597 			}
    598 
    599 			return SF_CONNECTING;
    600 		}
    601 		else
    602 		{
    603 			s->OnError(Anope::string(gnutls_strerror(ret)));
    604 			s->flags[SF_CONNECTING] = false;
    605 			s->flags[SF_DEAD] = true;
    606 			return SF_DEAD;
    607 		}
    608 	}
    609 	else
    610 	{
    611 		s->flags[SF_CONNECTING] = false;
    612 		s->flags[SF_CONNECTED] = true;
    613 		SocketEngine::Change(s, false, SF_WRITABLE);
    614 		SocketEngine::Change(s, true, SF_READABLE);
    615 		s->OnConnect();
    616 		return SF_CONNECTED;
    617 	}
    618 }
    619 
    620 void SSLSocketIO::Destroy()
    621 {
    622 	if (this->sess)
    623 	{
    624 		gnutls_bye(this->sess, GNUTLS_SHUT_WR);
    625 		gnutls_deinit(this->sess);
    626 	}
    627 
    628 	mycreds->decrref();
    629 
    630 	delete this;
    631 }
    632 
    633 SSLSocketIO::SSLSocketIO() : sess(NULL), mycreds(me->cred)
    634 {
    635 	mycreds->incrref();
    636 }
    637 
    638 #if (GNUTLS_VERSION_MAJOR < 2 || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR < 12))
    639 int GnuTLS::X509CertCredentials::cert_callback(gnutls_session_t sess, const gnutls_datum_t* req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t* sign_algos, int sign_algos_length, gnutls_retr_st* st)
    640 {
    641 	st->type = GNUTLS_CRT_X509;
    642 #else
    643 int GnuTLS::X509CertCredentials::cert_callback(gnutls_session_t sess, const gnutls_datum_t* req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t* sign_algos, int sign_algos_length, gnutls_retr2_st* st)
    644 {
    645 	st->cert_type = GNUTLS_CRT_X509;
    646 	st->key_type = GNUTLS_PRIVKEY_X509;
    647 #endif
    648 	st->ncerts = me->cred->certs.size();
    649 	st->cert.x509 = me->cred->certs.raw();
    650 	st->key.x509 = me->cred->key.get();
    651 	st->deinit_all = 0;
    652 
    653 	return 0;
    654 }
    655 
    656 MODULE_INIT(GnuTLSModule)