2 * Copyright (c) 2014 Alexander Golovko <alexandro@onsec.ru>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
28 #include <openssl/ssl.h>
29 #include <openssl/err.h>
37 static nscang_client_t
*nscang_client_instances
;
39 static pthread_rwlock_t nscang_client_instances_lock
=
40 PTHREAD_RWLOCK_INITIALIZER
;
43 set_psk(SSL
*ssl
, const char *hint
, char *identity
,
44 unsigned int max_identity_length
, unsigned char *psk
,
45 unsigned int max_psk_length
)
48 nscang_client_t
*c
= NULL
;
51 rc
= pthread_rwlock_rdlock(&nscang_client_instances_lock
);
52 if ((rc
!= EBUSY
) && (rc
!= EAGAIN
))
56 HASH_FIND_PTR(nscang_client_instances
, &ssl
, c
);
57 pthread_rwlock_unlock(&nscang_client_instances_lock
);
60 strncpy(identity
, c
->identity
, max_identity_length
);
61 identity
[max_identity_length
- 1] = 0x00;
63 strncpy((char *)psk
, c
->psk
, max_psk_length
);
64 psk
[max_psk_length
- 1] = 0x00;
66 return strlen((char *)psk
);
74 nscang_client_init(nscang_client_t
*c
, char *host
, int port
, char *ciphers
,
75 char *identity
, char *psk
)
79 memset(c
, 0x00, sizeof(nscang_client_t
));
81 c
->ssl_ctx
= SSL_CTX_new(SSLv23_client_method());
82 if (c
->ssl_ctx
== NULL
) {
83 c
->_errno
= NSCANG_ERROR_SSL_CTX_CREATE
;
87 if (ciphers
!= NULL
) {
88 if (SSL_CTX_set_cipher_list(c
->ssl_ctx
, ciphers
) != 1) {
89 c
->_errno
= NSCANG_ERROR_SSL_CIPHERS
;
94 SSL_CTX_set_options(c
->ssl_ctx
, SSL_OP_NO_SSLv2
| SSL_OP_NO_SSLv3
);
96 c
->bio
= BIO_new(BIO_s_connect());
98 c
->_errno
= NSCANG_ERROR_SSL_BIO_CREATE
;
101 BIO_set_conn_hostname(c
->bio
, host
);
102 BIO_set_conn_int_port(c
->bio
, &port
);
103 BIO_set_nbio(c
->bio
, 1);
105 c
->ssl
= SSL_new(c
->ssl_ctx
);
106 if (c
->ssl
== NULL
) {
107 nscang_client_free(c
);
108 c
->_errno
= NSCANG_ERROR_SSL_CREATE
;
112 SSL_set_bio(c
->ssl
, c
->bio
, c
->bio
);
113 SSL_set_connect_state(c
->ssl
);
114 SSL_set_psk_client_callback(c
->ssl
, set_psk
);
116 c
->identity
= malloc(strlen(identity
) + 1);
117 c
->psk
= malloc(strlen(psk
) + 1);
119 if ((c
->identity
== NULL
) || (c
->psk
== NULL
)) {
120 c
->_errno
= NSCANG_ERROR_MALLOC
;
124 strcpy(c
->identity
, identity
);
127 /* Busyloop until we get an rw lock. */
129 rc
= pthread_rwlock_wrlock(&nscang_client_instances_lock
);
134 c
->_errno
= NSCANG_ERROR_LOCKING
;
138 HASH_ADD_PTR(nscang_client_instances
, ssl
, c
);
139 pthread_rwlock_unlock(&nscang_client_instances_lock
);
141 c
->state
= NSCANG_STATE_NEW
;
147 nscang_client_free(nscang_client_t
*c
)
149 if (c
->state
!= NSCANG_STATE_NONE
)
150 nscang_client_disconnect(c
);
152 if (c
->ssl
!= NULL
) {
155 /* Busyloop until we get an rw lock. */
157 rc
= pthread_rwlock_wrlock
158 (&nscang_client_instances_lock
);
163 HASH_DEL(nscang_client_instances
, c
);
164 pthread_rwlock_unlock(&nscang_client_instances_lock
);
167 if (c
->ssl
!= NULL
) {
172 if (c
->ssl_ctx
!= NULL
) {
173 SSL_CTX_free(c
->ssl_ctx
);
176 if (c
->identity
!= NULL
) {
180 if (c
->psk
!= NULL
) {
185 c
->state
= NSCANG_STATE_NONE
;
189 nscang_client_write(nscang_client_t
*c
, void *buf
, int len
, int timeout
)
199 endtime
= time(NULL
) + timeout
;
202 if ((rc
= SSL_write(c
->ssl
, buf
, len
)) > 0)
205 if (!BIO_should_retry(c
->bio
)) {
206 c
->_errno
= NSCANG_ERROR_SSL
;
210 if (time(NULL
) > endtime
) {
211 c
->_errno
= NSCANG_ERROR_TIMEOUT
;
216 FD_SET(BIO_get_fd(c
->bio
, NULL
), &fdset
);
217 select(1, NULL
, &fdset
, NULL
, &tv
);
222 nscang_client_response(nscang_client_t
*c
, int timeout
)
235 endtime
= time(NULL
) + timeout
;
238 rc
= SSL_read(c
->ssl
, ptr
, 1024 - was_read
);
241 was_read
= was_read
+ rc
;
242 ptr
= buf
+ was_read
;
244 if (buf
[was_read
- 1] == '\n') {
245 if (buf
[was_read
- 2] == '\r')
246 buf
[was_read
- 2] = 0x00;
248 buf
[was_read
- 1] = 0x00;
253 if (was_read
== 1024) {
254 c
->_errno
= NSCANG_ERROR_TOO_LONG_RESPONSE
;
257 } else if (!BIO_should_retry(c
->bio
)) {
258 c
->_errno
= NSCANG_ERROR_SSL
;
262 if (time(NULL
) > endtime
) {
263 c
->_errno
= NSCANG_ERROR_TIMEOUT
;
268 FD_SET(BIO_get_fd(c
->bio
, NULL
), &fdset
);
269 select(1, &fdset
, NULL
, NULL
, &tv
);
272 if (strncmp(buf
, "MOIN", 4) == 0) {
273 if (strcmp(buf
, "MOIN 1") == 0) {
274 return NSCANG_RESP_MOIN
;
276 c
->_errno
= NSCANG_ERROR_BAD_PROTO_VERSION
;
277 strncpy(c
->errstr
, buf
+ 5, sizeof(c
->errstr
) - 1);
278 c
->errstr
[sizeof(c
->errstr
) - 1] = 0x00;
281 } else if (strncmp(buf
, "OKAY", 4) == 0) {
282 return NSCANG_RESP_OKAY
;
283 } else if (strncmp(buf
, "FAIL", 4) == 0) {
284 c
->_errno
= NSCANG_ERROR_FAIL
;
285 strncpy(c
->errstr
, buf
+ 5, sizeof(c
->errstr
) - 1);
286 c
->errstr
[sizeof(c
->errstr
) - 1] = 0x00;
288 } else if (strncmp(buf
, "BAIL", 4) == 0) {
289 nscang_client_disconnect(c
);
290 c
->_errno
= NSCANG_ERROR_BAIL
;
291 strncpy(c
->errstr
, buf
+ 5, sizeof(c
->errstr
) - 1);
292 c
->errstr
[sizeof(c
->errstr
) - 1] = 0x00;
295 char *msg
= "BAIL Unknown response!";
297 nscang_client_write(c
, msg
, sizeof(msg
), 0);
298 nscang_client_disconnect(c
);
299 c
->_errno
= NSCANG_ERROR_UNKNOWN_RESPONSE
;
300 strncpy(c
->errstr
, buf
, sizeof(c
->errstr
) - 1);
301 c
->errstr
[sizeof(c
->errstr
) - 1] = 0x00;
307 nscang_client_disconnect(nscang_client_t
*c
)
315 if (c
->state
== NSCANG_STATE_MOIN
)
316 nscang_client_send_quit(c
);
318 if ((SSL_shutdown(c
->ssl
) == -1) && BIO_should_retry(c
->bio
)) {
320 FD_SET(BIO_get_fd(c
->bio
, NULL
), &fdset
);
321 select(1, NULL
, &fdset
, NULL
, &tv
);
322 SSL_shutdown(c
->ssl
);
328 c
->state
= NSCANG_STATE_NEW
;
332 nscang_client_send_moin(nscang_client_t
*c
, int timeout
)
337 if (c
->state
== NSCANG_STATE_MOIN
)
340 if (c
->state
!= NSCANG_STATE_NEW
) {
341 c
->_errno
= NSCANG_ERROR_BAD_STATE
;
346 len
= snprintf(cmd
, sizeof(cmd
), "MOIN 1 %08ld%08ld\r\n", random(),
349 if (!nscang_client_write(c
, cmd
, len
, timeout
))
352 rc
= nscang_client_response(c
, timeout
);
357 if (rc
!= NSCANG_RESP_MOIN
) {
358 c
->_errno
= NSCANG_ERROR_PROTOCOL_MISMATCH
;
362 c
->state
= NSCANG_STATE_MOIN
;
367 nscang_client_send_push(nscang_client_t
*c
, char *host
, char *service
,
368 int status
, char *message
, int timeout
)
370 char cmd
[64], command
[1024];
373 if (!nscang_client_send_moin(c
, timeout
))
377 len
= snprintf(command
, sizeof(command
) - 1,
378 "[%u] PROCESS_HOST_CHECK_RESULT;%s;%d;%s",
379 (unsigned int)time(NULL
), host
, status
, message
);
381 len
= snprintf(command
, sizeof(command
) - 1,
382 "[%u] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s",
383 (unsigned int)time(NULL
), host
, service
, status
, message
);
385 command
[len
++] = '\n';
387 snprintf(cmd
, sizeof(cmd
), "PUSH %d\n", len
);
388 if (!nscang_client_write(c
, cmd
, strlen(cmd
), timeout
))
391 rc
= nscang_client_response(c
, timeout
);
396 if (rc
!= NSCANG_RESP_OKAY
) {
397 c
->_errno
= NSCANG_ERROR_PROTOCOL_MISMATCH
;
401 if (!nscang_client_write(c
, command
, len
, timeout
))
404 rc
= nscang_client_response(c
, timeout
);
409 if (rc
!= NSCANG_RESP_OKAY
) {
410 c
->_errno
= NSCANG_ERROR_PROTOCOL_MISMATCH
;
418 nscang_client_send_quit(nscang_client_t
*c
)
420 if (c
->state
!= NSCANG_STATE_MOIN
) {
421 c
->_errno
= NSCANG_ERROR_BAD_STATE
;
425 if (!nscang_client_write(c
, "QUIT\n", 5, 0))
432 nscang_client_errstr(nscang_client_t
*c
, char *buf
, int buf_size
)
435 case NSCANG_ERROR_SSL_CTX_CREATE
:
436 strncpy(buf
, "Can't create SSL context", buf_size
);
438 case NSCANG_ERROR_SSL_CIPHERS
:
439 strncpy(buf
, "Bad ciphers list", buf_size
);
441 case NSCANG_ERROR_SSL_BIO_CREATE
:
442 strncpy(buf
, "Can't create BIO socket", buf_size
);
444 case NSCANG_ERROR_SSL_CREATE
:
445 strncpy(buf
, "Can't create SSL", buf_size
);
447 case NSCANG_ERROR_SSL
:
448 snprintf(buf
, buf_size
, "SSL error - %s - %s",
449 ERR_reason_error_string(ERR_peek_last_error()),
450 ERR_reason_error_string(ERR_peek_error()));
452 case NSCANG_ERROR_MALLOC
:
453 strncpy(buf
, "Can't allocate memory", buf_size
);
455 case NSCANG_ERROR_TIMEOUT
:
456 strncpy(buf
, "Timeout was reached", buf_size
);
458 case NSCANG_ERROR_TOO_LONG_RESPONSE
:
459 strncpy(buf
, "Protocol mismatch - too long response", buf_size
);
461 case NSCANG_ERROR_BAD_PROTO_VERSION
:
462 snprintf(buf
, buf_size
, "Protocol mismatch - bad version '%s'",
465 case NSCANG_ERROR_PROTOCOL_MISMATCH
:
466 strncpy(buf
, "Protocol mismatch - unexpected server response",
469 case NSCANG_ERROR_UNKNOWN_RESPONSE
:
470 strncpy(buf
, "Protocol mismatch - unknown server response",
473 case NSCANG_ERROR_BAIL
:
474 snprintf(buf
, buf_size
, "BAIL: %s", c
->errstr
);
476 case NSCANG_ERROR_FAIL
:
477 snprintf(buf
, buf_size
, "FAIL: %s", c
->errstr
);
479 case NSCANG_ERROR_BAD_STATE
:
480 snprintf(buf
, buf_size
, "Operation not permitted in state %d",
483 case NSCANG_ERROR_LOCKING
:
484 strncpy(buf
, "Can't obtain lock for instances list", buf_size
);
489 buf
[buf_size
- 1] = 0x00;