Mention upsrw output change.
[networkupstools/kirr.git] / server / netssl.c
blobcbf0c7eb41a0cf5bf668d07f8cec3df2dbc346ae
1 /* netssl.c - Interface to OpenSSL for upsd
3 Copyright (C)
4 2002 Russell Kroll <rkroll@exploits.org>
5 2008 Arjen de Korte <adkorte-guest@alioth.debian.org>
7 based on the original implementation:
9 Copyright (C) 2002 Technorama Ltd. <oss-list-ups@technorama.net>
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include <sys/types.h>
27 #include <netinet/in.h>
28 #include <sys/socket.h>
30 #include "upsd.h"
31 #include "neterr.h"
32 #include "netssl.h"
34 #ifdef WITH_NSS
35 #include <pk11pub.h>
36 #include <prinit.h>
37 #include <private/pprio.h>
38 #include <key.h>
39 #include <keyt.h>
40 #include <secerr.h>
41 #include <sslerr.h>
42 #endif /* WITH_NSS */
44 char *certfile = NULL;
45 char *certname = NULL;
46 char *certpasswd = NULL;
48 #ifdef WITH_CLIENT_CERTIFICATE_VALIDATION
49 int certrequest = 0;
50 #endif /* WITH_CLIENT_CERTIFICATE_VALIDATION */
52 static int ssl_initialized = 0;
54 #ifndef WITH_SSL
56 /* stubs for non-ssl compiles */
57 void net_starttls(nut_ctype_t *client, int numarg, const char **arg)
59 send_err(client, NUT_ERR_FEATURE_NOT_SUPPORTED);
60 return;
63 int ssl_write(nut_ctype_t *client, const char *buf, size_t buflen)
65 upslogx(LOG_ERR, "ssl_write called but SSL wasn't compiled in");
66 return -1;
69 int ssl_read(nut_ctype_t *client, char *buf, size_t buflen)
71 upslogx(LOG_ERR, "ssl_read called but SSL wasn't compiled in");
72 return -1;
75 void ssl_init(void)
77 ssl_initialized = 0; /* keep gcc quiet */
80 void ssl_finish(nut_ctype_t *client)
82 if (client->ssl) {
83 upslogx(LOG_ERR, "ssl_finish found active SSL connection but SSL wasn't compiled in");
87 void ssl_cleanup(void)
91 #else
93 #ifdef WITH_OPENSSL
95 static SSL_CTX *ssl_ctx = NULL;
97 static void ssl_debug(void)
99 int e;
100 char errmsg[SMALLBUF];
102 while ((e = ERR_get_error()) != 0) {
103 ERR_error_string_n(e, errmsg, sizeof(errmsg));
104 upsdebugx(1, "ssl_debug: %s", errmsg);
108 static int ssl_error(SSL *ssl, int ret)
110 int e;
112 e = SSL_get_error(ssl, ret);
114 switch (e)
116 case SSL_ERROR_WANT_READ:
117 upsdebugx(1, "ssl_error() ret=%d SSL_ERROR_WANT_READ", ret);
118 break;
120 case SSL_ERROR_WANT_WRITE:
121 upsdebugx(1, "ssl_error() ret=%d SSL_ERROR_WANT_WRITE", ret);
122 break;
124 case SSL_ERROR_SYSCALL:
125 if (ret == 0 && ERR_peek_error() == 0) {
126 upsdebugx(1, "ssl_error() EOF from client");
127 } else {
128 upsdebugx(1, "ssl_error() ret=%d SSL_ERROR_SYSCALL", ret);
130 break;
132 default:
133 upsdebugx(1, "ssl_error() ret=%d SSL_ERROR %d", ret, e);
134 ssl_debug();
137 return -1;
140 #elif defined(WITH_NSS) /* WITH_OPENSSL */
142 static CERTCertificate *cert;
143 static SECKEYPrivateKey *privKey;
145 static char *nss_password_callback(PK11SlotInfo *slot, PRBool retry,
146 void *arg)
148 if (retry) {
149 /* Force not inted to retrieve password many times. */
150 return NULL;
152 upslogx(LOG_INFO, "Intend to retrieve password for %s / %s: password %sconfigured",
153 PK11_GetSlotName(slot), PK11_GetTokenName(slot), certpasswd?"":"not ");
154 return certpasswd ? PL_strdup(certpasswd) : NULL;
157 static void nss_error(const char* text)
159 char buffer[SMALLBUF];
160 PRInt32 length = PR_GetErrorText(buffer);
161 if (length > 0 && length < SMALLBUF) {
162 upsdebugx(1, "nss_error %ld in %s : %s", (long)PR_GetError(), text, buffer);
163 }else{
164 upsdebugx(1, "nss_error %ld in %s", (long)PR_GetError(), text);
168 static int ssl_error(PRFileDesc *ssl, int ret)
170 char buffer[256];
171 PRInt32 length;
172 PRErrorCode e;
174 e = PR_GetError();
175 length = PR_GetErrorText(buffer);
176 if (length > 0 && length < 256) {
177 upsdebugx(1, "ssl_error() ret=%d %*s", e, length, buffer);
178 } else {
179 upsdebugx(1, "ssl_error() ret=%d", e);
182 return -1;
185 static SECStatus AuthCertificate(CERTCertDBHandle *arg, PRFileDesc *fd,
186 PRBool checksig, PRBool isServer)
188 nut_ctype_t *client = (nut_ctype_t *)SSL_RevealPinArg(fd);
189 SECStatus status = SSL_AuthCertificate(arg, fd, checksig, isServer);
190 upslogx(LOG_INFO, "Intend to authenticate client %s : %s.",
191 client?client->addr:"(unnamed)",
192 status==SECSuccess?"SUCCESS":"FAILED");
193 return status;
196 static SECStatus BadCertHandler(nut_ctype_t *arg, PRFileDesc *fd)
198 upslogx(LOG_WARNING, "Certificate validation failed for %s",
199 (arg&&arg->addr)?arg->addr:"<unnamed>");
200 #ifdef WITH_CLIENT_CERTIFICATE_VALIDATION
201 /* BadCertHandler is called when the NSS certificate validation is failed.
202 * If the certificate verification (user conf) is mandatory, reject authentication
203 * else accept it.
205 return certrequest==NETSSL_CERTREQ_REQUIRE?SECFailure:SECSuccess;
206 #else /* WITH_CLIENT_CERTIFICATE_VALIDATION */
207 /* Always accept clients. */
208 return SECSuccess;
209 #endif /* WITH_CLIENT_CERTIFICATE_VALIDATION */
212 static void HandshakeCallback(PRFileDesc *fd, nut_ctype_t *client_data)
214 upslogx(LOG_INFO, "SSL handshake done successfully with client %s",
215 client_data->addr);
219 #endif /* WITH_OPENSSL | WITH_NSS */
221 void net_starttls(nut_ctype_t *client, int numarg, const char **arg)
223 #ifdef WITH_OPENSSL
224 int ret;
225 #elif defined(WITH_NSS) /* WITH_OPENSSL */
226 SECStatus status;
227 PRFileDesc *socket;
228 #endif /* WITH_OPENSSL | WITH_NSS */
230 if (client->ssl) {
231 send_err(client, NUT_ERR_ALREADY_SSL_MODE);
232 return;
235 client->ssl_connected = 0;
237 if ((!certfile) || (!ssl_initialized)) {
238 send_err(client, NUT_ERR_FEATURE_NOT_CONFIGURED);
239 return;
242 #ifdef WITH_OPENSSL
243 if (!ssl_ctx) {
244 #elif defined(WITH_NSS) /* WITH_OPENSSL */
245 if (!NSS_IsInitialized()) {
246 #endif /* WITH_OPENSSL | WITH_NSS */
247 send_err(client, NUT_ERR_FEATURE_NOT_CONFIGURED);
248 ssl_initialized = 0;
249 return;
252 if (!sendback(client, "OK STARTTLS\n")) {
253 return;
256 #ifdef WITH_OPENSSL
258 client->ssl = SSL_new(ssl_ctx);
260 if (!client->ssl) {
261 upslog_with_errno(LOG_ERR, "SSL_new failed\n");
262 ssl_debug();
263 return;
266 if (SSL_set_fd(client->ssl, client->sock_fd) != 1) {
267 upslog_with_errno(LOG_ERR, "SSL_set_fd failed\n");
268 ssl_debug();
269 return;
272 ret = SSL_accept(client->ssl);
273 switch (ret)
275 case 1:
276 client->ssl_connected = 1;
277 upsdebugx(3, "SSL connected");
278 break;
280 case 0:
281 upslog_with_errno(LOG_ERR, "SSL_accept do not accept handshake.");
282 ssl_error(client->ssl, ret);
283 break;
284 case -1:
285 upslog_with_errno(LOG_ERR, "Unknown return value from SSL_accept");
286 ssl_error(client->ssl, ret);
287 break;
290 #elif defined(WITH_NSS) /* WITH_OPENSSL */
292 socket = PR_ImportTCPSocket(client->sock_fd);
293 if (socket == NULL){
294 upslogx(LOG_ERR, "Can not inialize SSL connection");
295 nss_error("net_starttls / PR_ImportTCPSocket");
296 return;
299 client->ssl = SSL_ImportFD(NULL, socket);
300 if (client->ssl == NULL){
301 upslogx(LOG_ERR, "Can not inialize SSL connection");
302 nss_error("net_starttls / SSL_ImportFD");
303 return;
306 if (SSL_SetPKCS11PinArg(client->ssl, client) == -1){
307 upslogx(LOG_ERR, "Can not inialize SSL connection");
308 nss_error("net_starttls / SSL_SetPKCS11PinArg");
309 return;
312 /* Note cast to SSLAuthCertificate to prevent warning due to
313 * bad function prototype in NSS.
315 status = SSL_AuthCertificateHook(client->ssl, (SSLAuthCertificate)AuthCertificate, CERT_GetDefaultCertDB());
316 if (status != SECSuccess) {
317 upslogx(LOG_ERR, "Can not inialize SSL connection");
318 nss_error("net_starttls / SSL_AuthCertificateHook");
319 return;
322 status = SSL_BadCertHook(client->ssl, (SSLBadCertHandler)BadCertHandler, client);
323 if (status != SECSuccess) {
324 upslogx(LOG_ERR, "Can not inialize SSL connection");
325 nss_error("net_starttls / SSL_BadCertHook");
326 return;
329 status = SSL_HandshakeCallback(client->ssl, (SSLHandshakeCallback)HandshakeCallback, client);
330 if (status != SECSuccess) {
331 upslogx(LOG_ERR, "Can not inialize SSL connection");
332 nss_error("net_starttls / SSL_HandshakeCallback");
333 return;
336 status = SSL_ConfigSecureServer(client->ssl, cert, privKey, NSS_FindCertKEAType(cert));
337 if (status != SECSuccess) {
338 upslogx(LOG_ERR, "Can not inialize SSL connection");
339 nss_error("net_starttls / SSL_ConfigSecureServer");
340 return;
343 status = SSL_ResetHandshake(client->ssl, PR_TRUE);
344 if (status != SECSuccess) {
345 upslogx(LOG_ERR, "Can not inialize SSL connection");
346 nss_error("net_starttls / SSL_ResetHandshake");
347 return;
350 /* Note: this call can generate memory leaks not resolvable
351 * by any release function.
352 * Probably SSL session key object allocation. */
353 status = SSL_ForceHandshake(client->ssl);
354 if (status != SECSuccess) {
355 PRErrorCode code = PR_GetError();
356 if (code==SSL_ERROR_NO_CERTIFICATE) {
357 upslogx(LOG_WARNING, "Client %s do not provide certificate.",
358 client->addr);
359 } else {
360 nss_error("net_starttls / SSL_ForceHandshake");
361 /* TODO : Close the connection. */
362 return;
365 client->ssl_connected = 1;
366 #endif /* WITH_OPENSSL | WITH_NSS */
369 void ssl_init(void)
371 #ifdef WITH_NSS
372 SECStatus status;
373 #elif defined(WITH_OPENSSL)
374 #if OPENSSL_VERSION_NUMBER >= 0x10000000L
375 const SSL_METHOD *ssl_method;
376 #else
377 SSL_METHOD *ssl_method;
378 #endif
379 #endif /* WITH_NSS|WITH_OPENSSL */
381 if (!certfile) {
382 return;
385 check_perms(certfile);
387 #ifdef WITH_OPENSSL
389 SSL_load_error_strings();
390 SSL_library_init();
392 if ((ssl_method = TLSv1_server_method()) == NULL) {
393 ssl_debug();
394 fatalx(EXIT_FAILURE, "TLSv1_server_method failed");
397 if ((ssl_ctx = SSL_CTX_new(ssl_method)) == NULL) {
398 ssl_debug();
399 fatalx(EXIT_FAILURE, "SSL_CTX_new failed");
402 if (SSL_CTX_use_certificate_chain_file(ssl_ctx, certfile) != 1) {
403 ssl_debug();
404 fatalx(EXIT_FAILURE, "SSL_CTX_use_certificate_chain_file(%s) failed", certfile);
407 if (SSL_CTX_use_PrivateKey_file(ssl_ctx, certfile, SSL_FILETYPE_PEM) != 1) {
408 ssl_debug();
409 fatalx(EXIT_FAILURE, "SSL_CTX_use_PrivateKey_file(%s) failed", certfile);
412 if (SSL_CTX_check_private_key(ssl_ctx) != 1) {
413 ssl_debug();
414 fatalx(EXIT_FAILURE, "SSL_CTX_check_private_key(%s) failed", certfile);
417 if (SSL_CTX_set_cipher_list(ssl_ctx, "HIGH:@STRENGTH") != 1) {
418 ssl_debug();
419 fatalx(EXIT_FAILURE, "SSL_CTX_set_cipher_list failed");
422 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
424 ssl_initialized = 1;
426 #elif defined(WITH_NSS) /* WITH_OPENSSL */
428 if (!certname || certname[0]==0 ) {
429 upslogx(LOG_ERR, "The SSL certificate name is not specified.");
430 return;
433 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
435 PK11_SetPasswordFunc(nss_password_callback);
437 if (certfile)
438 /* Note: this call can generate memory leaks not resolvable
439 * by any release function.
440 * Probably NSS key module object allocation and
441 * probably NSS key db object allocation too. */
442 status = NSS_Init(certfile);
443 else
444 status = NSS_NoDB_Init(NULL);
445 if (status != SECSuccess) {
446 upslogx(LOG_ERR, "Can not initialize SSL context");
447 nss_error("upscli_init / NSS_[NoDB]_Init");
448 return;
451 status = NSS_SetDomesticPolicy();
452 if (status != SECSuccess) {
453 upslogx(LOG_ERR, "Can not initialize SSL policy");
454 nss_error("upscli_init / NSS_SetDomesticPolicy");
455 return;
458 /* Default server cache config */
459 status = SSL_ConfigServerSessionIDCache(0, 0, 0, NULL);
460 if (status != SECSuccess) {
461 upslogx(LOG_ERR, "Can not initialize SSL server cache");
462 nss_error("upscli_init / SSL_ConfigServerSessionIDCache");
463 return;
466 status = SSL_OptionSetDefault(SSL_ENABLE_SSL3, PR_TRUE);
467 if (status != SECSuccess) {
468 upslogx(LOG_ERR, "Can not enable SSLv3");
469 nss_error("upscli_init / SSL_OptionSetDefault(SSL_ENABLE_SSL3)");
470 return;
472 status = SSL_OptionSetDefault(SSL_ENABLE_TLS, PR_TRUE);
473 if (status != SECSuccess) {
474 upslogx(LOG_ERR, "Can not enable TLSv1");
475 nss_error("upscli_init / SSL_OptionSetDefault(SSL_ENABLE_TLS)");
476 return;
479 #ifdef WITH_CLIENT_CERTIFICATE_VALIDATION
480 if (certrequest < NETSSL_CERTREQ_NO &&
481 certrequest > NETSSL_CERTREQ_REQUEST) {
482 upslogx(LOG_ERR, "Invalid certificate requirement");
483 return;
486 if (certrequest == NETSSL_CERTREQ_REQUEST ||
487 certrequest == NETSSL_CERTREQ_REQUIRE ) {
488 status = SSL_OptionSetDefault(SSL_REQUEST_CERTIFICATE, PR_TRUE);
489 if (status != SECSuccess) {
490 upslogx(LOG_ERR, "Can not enable certificate request");
491 nss_error("upscli_init / SSL_OptionSetDefault(SSL_REQUEST_CERTIFICATE)");
492 return;
496 if (certrequest == NETSSL_CERTREQ_REQUIRE ) {
497 status = SSL_OptionSetDefault(SSL_REQUIRE_CERTIFICATE, PR_TRUE);
498 if (status != SECSuccess) {
499 upslogx(LOG_ERR, "Can not enable certificate requirement");
500 nss_error("upscli_init / SSL_OptionSetDefault(SSL_REQUIRE_CERTIFICATE)");
501 return;
504 #endif /* WITH_CLIENT_CERTIFICATE_VALIDATION */
506 cert = PK11_FindCertFromNickname(certname, NULL);
507 if(cert==NULL) {
508 upslogx(LOG_ERR, "Can not find server certificate");
509 nss_error("upscli_init / PK11_FindCertFromNickname");
510 return;
513 privKey = PK11_FindKeyByAnyCert(cert, NULL);
514 if(privKey==NULL){
515 upslogx(LOG_ERR, "Can not find private key associate to server certificate");
516 nss_error("upscli_init / PK11_FindKeyByAnyCert");
517 return;
520 ssl_initialized = 1;
521 #else /* WITH_OPENSSL | WITH_NSS */
522 upslogx(LOG_ERR, "ssl_init called but SSL wasn't compiled in");
523 #endif /* WITH_OPENSSL | WITH_NSS */
526 int ssl_read(nut_ctype_t *client, char *buf, size_t buflen)
528 int ret;
530 if (!client->ssl_connected) {
531 return -1;
534 #ifdef WITH_OPENSSL
535 ret = SSL_read(client->ssl, buf, buflen);
536 #elif defined(WITH_NSS) /* WITH_OPENSSL */
537 ret = PR_Read(client->ssl, buf, buflen);
538 #endif /* WITH_OPENSSL | WITH_NSS */
540 if (ret < 1) {
541 ssl_error(client->ssl, ret);
542 return -1;
545 return ret;
548 int ssl_write(nut_ctype_t *client, const char *buf, size_t buflen)
550 int ret;
552 if (!client->ssl_connected) {
553 return -1;
556 #ifdef WITH_OPENSSL
557 ret = SSL_write(client->ssl, buf, buflen);
558 #elif defined(WITH_NSS) /* WITH_OPENSSL */
559 ret = PR_Write(client->ssl, buf, buflen);
560 #endif /* WITH_OPENSSL | WITH_NSS */
562 upsdebugx(5, "ssl_write ret=%d", ret);
564 return ret;
567 void ssl_finish(nut_ctype_t *client)
569 if (client->ssl) {
570 #ifdef WITH_OPENSSL
571 SSL_free(client->ssl);
572 #elif defined(WITH_NSS)
573 PR_Shutdown(client->ssl, PR_SHUTDOWN_BOTH);
574 PR_Close(client->ssl);
575 #endif /* WITH_OPENSSL | WITH_NSS */
576 client->ssl_connected = 0;
577 client->ssl = NULL;
581 void ssl_cleanup(void)
583 #ifdef WITH_OPENSSL
584 if (ssl_ctx) {
585 SSL_CTX_free(ssl_ctx);
586 ssl_ctx = NULL;
588 #elif defined(WITH_NSS) /* WITH_OPENSSL */
589 CERT_DestroyCertificate(cert);
590 SECKEY_DestroyPrivateKey(privKey);
591 NSS_Shutdown();
592 PR_Cleanup();
593 /* Called to release memory arena used by NSS/NSPR.
594 * Prevent to show all PL_ArenaAllocate mem alloc as leaks.
595 * https://developer.mozilla.org/en/NSS_Memory_allocation
597 PL_ArenaFinish();
598 #endif /* WITH_OPENSSL | WITH_NSS */
599 ssl_initialized = 0;
602 #endif /* WITH_SSL */