1 /* upsclient - network communications functions for UPS clients
2
3 Copyright (C)
4 2002 Russell Kroll <rkroll@exploits.org>
5 2008 Arjen de Korte <adkorte-guest@alioth.debian.org>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 #include "config.h" /* safe because it doesn't contain prototypes */
23 #include "nut_platform.h"
24
25 #ifdef HAVE_PTHREAD
26 /* this include is needed on AIX to have errno stored in thread local storage */
27 #include <pthread.h>
28 #endif
29
30 #include <errno.h>
31 #include <netdb.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39 #include <fcntl.h>
40
41 #include "upsclient.h"
42 #include "common.h"
43 #include "nut_stdint.h"
44 #include "timehead.h"
45
46 /* WA for Solaris/i386 bug: non-blocking connect sets errno to ENOENT */
47 #if (defined NUT_PLATFORM_SOLARIS)
48 #define SOLARIS_i386_NBCONNECT_ENOENT(status) ( (!strcmp("i386", CPU_TYPE)) ? (ENOENT == (status)) : 0 )
49 #else
50 #define SOLARIS_i386_NBCONNECT_ENOENT(status) (0)
51 #endif /* end of Solaris/i386 WA for non-blocking connect */
52
53 /* WA for AIX bug: non-blocking connect sets errno to 0 */
54 #if (defined NUT_PLATFORM_AIX)
55 #define AIX_NBCONNECT_0(status) (0 == (status))
56 #else
57 #define AIX_NBCONNECT_0(status) (0)
58 #endif /* end of AIX WA for non-blocking connect */
59
60 #ifdef WITH_NSS
61 #include <prerror.h>
62 #include <prinit.h>
63 #include <pk11func.h>
64 #include <prtypes.h>
65 #include <ssl.h>
66 #include <private/pprio.h>
67 #endif /* WITH_NSS */
68
69 #define UPSCLIENT_MAGIC 0x19980308
70
71 #define SMALLBUF 512
72
73 #ifdef SHUT_RDWR
74 #define shutdown_how SHUT_RDWR
75 #else
76 #define shutdown_how 2
77 #endif
78
79 static struct {
80 int flags;
81 const char *str;
82 } upscli_errlist[] =
83 {
84 { 0, "Unknown error" }, /* 0: UPSCLI_ERR_UNKNOWN */
85 { 0, "Variable not supported by UPS" }, /* 1: UPSCLI_ERR_VARNOTSUPP */
86 { 0, "No such host" }, /* 2: UPSCLI_ERR_NOSUCHHOST */
87 { 0, "Invalid response from server" }, /* 3: UPSCLI_ERR_INVRESP */
88 { 0, "Unknown UPS" }, /* 4: UPSCLI_ERR_UNKNOWNUPS */
89 { 0, "Invalid list type" }, /* 5: UPSCLI_ERR_INVLISTTYPE */
90 { 0, "Access denied" }, /* 6: UPSCLI_ERR_ACCESSDENIED */
91 { 0, "Password required" }, /* 7: UPSCLI_ERR_PWDREQUIRED */
92 { 0, "Password incorrect" }, /* 8: UPSCLI_ERR_PWDINCORRECT */
93 { 0, "Missing argument" }, /* 9: UPSCLI_ERR_MISSINGARG */
94 { 0, "Data stale" }, /* 10: UPSCLI_ERR_DATASTALE */
95 { 0, "Variable unknown" }, /* 11: UPSCLI_ERR_VARUNKNOWN */
96 { 0, "Already logged in" }, /* 12: UPSCLI_ERR_LOGINTWICE */
97 { 0, "Already set password" }, /* 13: UPSCLI_ERR_PWDSETTWICE */
98 { 0, "Unknown variable type" }, /* 14: UPSCLI_ERR_UNKNOWNTYPE */
99 { 0, "Unknown variable" }, /* 15: UPSCLI_ERR_UNKNOWNVAR */
100 { 0, "Read-only variable" }, /* 16: UPSCLI_ERR_VARREADONLY */
101 { 0, "New value is too long" }, /* 17: UPSCLI_ERR_TOOLONG */
102 { 0, "Invalid value for variable" }, /* 18: UPSCLI_ERR_INVALIDVALUE */
103 { 0, "Set command failed" }, /* 19: UPSCLI_ERR_SETFAILED */
104 { 0, "Unknown instant command" }, /* 20: UPSCLI_ERR_UNKINSTCMD */
105 { 0, "Instant command failed" }, /* 21: UPSCLI_ERR_CMDFAILED */
106 { 0, "Instant command not supported" }, /* 22: UPSCLI_ERR_CMDNOTSUPP */
107 { 0, "Invalid username" }, /* 23: UPSCLI_ERR_INVUSERNAME */
108 { 0, "Already set username" }, /* 24: UPSCLI_ERR_USERSETTWICE */
109 { 0, "Unknown command" }, /* 25: UPSCLI_ERR_UNKCOMMAND */
110 { 0, "Invalid argument" }, /* 26: UPSCLI_ERR_INVALIDARG */
111 { 1, "Send failure: %s" }, /* 27: UPSCLI_ERR_SENDFAILURE */
112 { 1, "Receive failure: %s" }, /* 28: UPSCLI_ERR_RECVFAILURE */
113 { 1, "socket failure: %s" }, /* 29: UPSCLI_ERR_SOCKFAILURE */
114 { 1, "bind failure: %s" }, /* 30: UPSCLI_ERR_BINDFAILURE */
115 { 1, "Connection failure: %s" }, /* 31: UPSCLI_ERR_CONNFAILURE */
116 { 1, "Write error: %s" }, /* 32: UPSCLI_ERR_WRITE */
117 { 1, "Read error: %s" }, /* 33: UPSCLI_ERR_READ */
118 { 0, "Invalid password" }, /* 34: UPSCLI_ERR_INVPASSWORD */
119 { 0, "Username required" }, /* 35: UPSCLI_ERR_USERREQUIRED */
120 { 0, "SSL is not available", }, /* 36: UPSCLI_ERR_SSLFAIL */
121 { 2, "SSL error: %s", }, /* 37: UPSCLI_ERR_SSLERR */
122 { 0, "Server disconnected", }, /* 38: UPSCLI_ERR_SRVDISC */
123 { 0, "Driver not connected", }, /* 39: UPSCLI_ERR_DRVNOTCONN */
124 { 0, "Memory allocation failure", }, /* 40: UPSCLI_ERR_NOMEM */
125 { 3, "Parse error: %s", }, /* 41: UPSCLI_ERR_PARSE */
126 { 0, "Protocol error", }, /* 42: UPSCLI_ERR_PROTOCOL */
127 };
128
129
130 typedef struct HOST_CERT_s {
131 const char *host;
132 const char *certname;
133 int certverify;
134 int forcessl;
135
136 struct HOST_CERT_s *next;
137 } HOST_CERT_t;
138 static HOST_CERT_t* upscli_find_host_cert(const char* hostname);
139
140
141 static int upscli_initialized = 0;
142
143 #ifdef WITH_OPENSSL
144 static SSL_CTX *ssl_ctx;
145 #elif defined(WITH_NSS) /* WITH_OPENSLL */
146 static int verify_certificate = 1;
147 static HOST_CERT_t *first_host_cert = NULL;
148 static char* nsscertname = NULL;
149 static char* nsscertpasswd = NULL;
150 #endif /* WITH_OPENSSL | WITH_NSS */
151
152
153 #ifdef WITH_OPENSSL
154
ssl_debug(void)155 static void ssl_debug(void)
156 {
157 unsigned long e;
158 char errmsg[SMALLBUF];
159
160 while ((e = ERR_get_error()) != 0) {
161 ERR_error_string_n(e, errmsg, sizeof(errmsg));
162 upsdebugx(2, "ssl_debug: %s", errmsg);
163 }
164 }
165
ssl_error(SSL * ssl,ssize_t ret)166 static int ssl_error(SSL *ssl, ssize_t ret)
167 {
168 int e;
169
170 if (ret >= INT_MAX) {
171 upslogx(LOG_ERR, "ssl_error() ret=%zd would not fit in an int", ret);
172 return -1;
173 }
174 e = SSL_get_error(ssl, (int)ret);
175
176 switch (e)
177 {
178 case SSL_ERROR_WANT_READ:
179 upslogx(LOG_ERR, "ssl_error() ret=%zd SSL_ERROR_WANT_READ", ret);
180 break;
181
182 case SSL_ERROR_WANT_WRITE:
183 upslogx(LOG_ERR, "ssl_error() ret=%zd SSL_ERROR_WANT_WRITE", ret);
184 break;
185
186 case SSL_ERROR_SYSCALL:
187 if (ret == 0 && ERR_peek_error() == 0) {
188 upslogx(LOG_ERR, "ssl_error() EOF from client");
189 } else {
190 upslogx(LOG_ERR, "ssl_error() ret=%zd SSL_ERROR_SYSCALL", ret);
191 }
192 break;
193
194 default:
195 upslogx(LOG_ERR, "ssl_error() ret=%zd SSL_ERROR %d", ret, e);
196 ssl_debug();
197 }
198
199 return -1;
200 }
201
202 #elif defined(WITH_NSS) /* WITH_OPENSSL */
203
nss_password_callback(PK11SlotInfo * slot,PRBool retry,void * arg)204 static char *nss_password_callback(PK11SlotInfo *slot, PRBool retry,
205 void *arg)
206 {
207 NUT_UNUSED_VARIABLE(retry);
208 NUT_UNUSED_VARIABLE(arg);
209
210 upslogx(LOG_INFO, "Intend to retrieve password for %s / %s: password %sconfigured",
211 PK11_GetSlotName(slot), PK11_GetTokenName(slot), nsscertpasswd?"":"not ");
212 return nsscertpasswd ? PL_strdup(nsscertpasswd) : NULL;
213 }
214
nss_error(const char * funcname)215 static void nss_error(const char* funcname)
216 {
217 char buffer[SMALLBUF];
218 PRInt32 length = PR_GetErrorText(buffer);
219 if (length > 0 && length < SMALLBUF) {
220 upsdebugx(1, "nss_error %ld in %s : %s", (long)PR_GetError(), funcname, buffer);
221 }else{
222 upsdebugx(1, "nss_error %ld in %s", (long)PR_GetError(), funcname);
223 }
224 }
225
AuthCertificate(CERTCertDBHandle * arg,PRFileDesc * fd,PRBool checksig,PRBool isServer)226 static SECStatus AuthCertificate(CERTCertDBHandle *arg, PRFileDesc *fd,
227 PRBool checksig, PRBool isServer)
228 {
229 UPSCONN_t *ups = (UPSCONN_t *)SSL_RevealPinArg(fd);
230 SECStatus status = SSL_AuthCertificate(arg, fd, checksig, isServer);
231 upslogx(LOG_INFO, "Intend to authenticate server %s : %s",
232 ups?ups->host:"<unnamed>",
233 status==SECSuccess?"SUCCESS":"FAILED");
234 if (status != SECSuccess) {
235 nss_error("SSL_AuthCertificate");
236 }
237 return status;
238 }
239
AuthCertificateDontVerify(CERTCertDBHandle * arg,PRFileDesc * fd,PRBool checksig,PRBool isServer)240 static SECStatus AuthCertificateDontVerify(CERTCertDBHandle *arg, PRFileDesc *fd,
241 PRBool checksig, PRBool isServer)
242 {
243 UPSCONN_t *ups = (UPSCONN_t *)SSL_RevealPinArg(fd);
244 NUT_UNUSED_VARIABLE(arg);
245 NUT_UNUSED_VARIABLE(checksig);
246 NUT_UNUSED_VARIABLE(isServer);
247
248 upslogx(LOG_INFO, "Do not intend to authenticate server %s",
249 ups?ups->host:"<unnamed>");
250 return SECSuccess;
251 }
252
BadCertHandler(UPSCONN_t * arg,PRFileDesc * fd)253 static SECStatus BadCertHandler(UPSCONN_t *arg, PRFileDesc *fd)
254 {
255 HOST_CERT_t* cert;
256 NUT_UNUSED_VARIABLE(fd);
257
258 upslogx(LOG_WARNING, "Certificate validation failed for %s",
259 (arg&&arg->host)?arg->host:"<unnamed>");
260 /* BadCertHandler is called when the NSS certificate validation is failed.
261 * If the certificate verification (user conf) is mandatory, reject authentication
262 * else accept it.
263 */
264 cert = upscli_find_host_cert(arg->host);
265 if (cert != NULL) {
266 return cert->certverify==0 ? SECSuccess : SECFailure;
267 } else {
268 return verify_certificate==0 ? SECSuccess : SECFailure;
269 }
270 }
271
GetClientAuthData(UPSCONN_t * arg,PRFileDesc * fd,CERTDistNames * caNames,CERTCertificate ** pRetCert,SECKEYPrivateKey ** pRetKey)272 static SECStatus GetClientAuthData(UPSCONN_t *arg, PRFileDesc *fd,
273 CERTDistNames *caNames, CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey)
274 {
275 CERTCertificate *cert;
276 SECKEYPrivateKey *privKey;
277 SECStatus status = NSS_GetClientAuthData(arg, fd, caNames, pRetCert, pRetKey);
278 if (status == SECFailure) {
279 if (nsscertname != NULL) {
280 cert = PK11_FindCertFromNickname(nsscertname, NULL);
281 if(cert==NULL) {
282 upslogx(LOG_ERR, "Can not find self-certificate");
283 nss_error("GetClientAuthData / PK11_FindCertFromNickname");
284 }else{
285 privKey = PK11_FindKeyByAnyCert(cert, NULL);
286 if(privKey==NULL){
287 upslogx(LOG_ERR, "Can not find private key related to self-certificate");
288 nss_error("GetClientAuthData / PK11_FindKeyByAnyCert");
289 }else{
290 *pRetCert = cert;
291 *pRetKey = privKey;
292 status = SECSuccess;
293 }
294 }
295 } else {
296 upslogx(LOG_ERR, "Self-certificate name not configured");
297 }
298 }
299
300 return status;
301 }
302
HandshakeCallback(PRFileDesc * fd,UPSCONN_t * client_data)303 static void HandshakeCallback(PRFileDesc *fd, UPSCONN_t *client_data)
304 {
305 NUT_UNUSED_VARIABLE(fd);
306
307 upslogx(LOG_INFO, "SSL handshake done successfully with server %s",
308 client_data->host);
309 }
310
311 #endif /* WITH_OPENSSL | WITH_NSS */
312
upscli_init(int certverify,const char * certpath,const char * certname,const char * certpasswd)313 int upscli_init(int certverify, const char *certpath,
314 const char *certname, const char *certpasswd)
315 {
316 #ifdef WITH_OPENSSL
317 int ret, ssl_mode = SSL_VERIFY_NONE;
318 NUT_UNUSED_VARIABLE(certname);
319 NUT_UNUSED_VARIABLE(certpasswd);
320 #elif defined(WITH_NSS) /* WITH_OPENSSL */
321 SECStatus status;
322 #else
323 NUT_UNUSED_VARIABLE(certverify);
324 NUT_UNUSED_VARIABLE(certpath);
325 NUT_UNUSED_VARIABLE(certname);
326 NUT_UNUSED_VARIABLE(certpasswd);
327 #endif /* WITH_OPENSSL | WITH_NSS */
328
329 if (upscli_initialized == 1) {
330 upslogx(LOG_WARNING, "upscli already initialized");
331 return -1;
332 }
333
334 #ifdef WITH_OPENSSL
335
336 #if OPENSSL_VERSION_NUMBER < 0x10100000L
337 SSL_load_error_strings();
338 SSL_library_init();
339
340 ssl_ctx = SSL_CTX_new(SSLv23_client_method());
341 #else
342 ssl_ctx = SSL_CTX_new(TLS_client_method());
343 #endif
344
345 if (!ssl_ctx) {
346 upslogx(LOG_ERR, "Can not initialize SSL context");
347 return -1;
348 }
349
350 #if OPENSSL_VERSION_NUMBER < 0x10100000L
351 /* set minimum protocol TLSv1 */
352 SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
353 #else
354 ret = SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_VERSION);
355 if (ret != 1) {
356 upslogx(LOG_ERR, "Can not set minimum protocol to TLSv1");
357 return -1;
358 }
359 #endif
360
361 if (!certpath) {
362 if (certverify == 1) {
363 upslogx(LOG_ERR, "Can not verify certificate if any is specified");
364 return -1; /* Failed : cert is mandatory but no certfile */
365 }
366 } else {
367 switch(certverify) {
368
369 case 0:
370 ssl_mode = SSL_VERIFY_NONE;
371 break;
372 default:
373 ssl_mode = SSL_VERIFY_PEER;
374 break;
375 }
376
377 ret = SSL_CTX_load_verify_locations(ssl_ctx, NULL, certpath);
378 if (ret != 1) {
379 upslogx(LOG_ERR, "Failed to load certificate from pemfile %s", certpath);
380 return -1;
381 }
382
383 SSL_CTX_set_verify(ssl_ctx, ssl_mode, NULL);
384 }
385 #elif defined(WITH_NSS) /* WITH_OPENSSL */
386 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
387
388 PK11_SetPasswordFunc(nss_password_callback);
389
390 if (certpath) {
391 upslogx(LOG_INFO, "Init SSL with cerificate database located at %s", certpath);
392 status = NSS_Init(certpath);
393 } else {
394 upslogx(LOG_NOTICE, "Init SSL without certificate database");
395 status = NSS_NoDB_Init(NULL);
396 }
397 if (status != SECSuccess) {
398 upslogx(LOG_ERR, "Can not initialize SSL context");
399 nss_error("upscli_init / NSS_[NoDB]_Init");
400 return -1;
401 }
402
403 status = NSS_SetDomesticPolicy();
404 if (status != SECSuccess) {
405 upslogx(LOG_ERR, "Can not initialize SSL policy");
406 nss_error("upscli_init / NSS_SetDomesticPolicy");
407 return -1;
408 }
409
410 SSL_ClearSessionCache();
411
412 status = SSL_OptionSetDefault(SSL_ENABLE_SSL3, PR_TRUE);
413 if (status != SECSuccess) {
414 upslogx(LOG_ERR, "Can not enable SSLv3");
415 nss_error("upscli_init / SSL_OptionSetDefault(SSL_ENABLE_SSL3)");
416 return -1;
417 }
418 status = SSL_OptionSetDefault(SSL_ENABLE_TLS, PR_TRUE);
419 if (status != SECSuccess) {
420 upslogx(LOG_ERR, "Can not enable TLSv1");
421 nss_error("upscli_init / SSL_OptionSetDefault(SSL_ENABLE_TLS)");
422 return -1;
423 }
424 status = SSL_OptionSetDefault(SSL_V2_COMPATIBLE_HELLO, PR_FALSE);
425 if (status != SECSuccess) {
426 upslogx(LOG_ERR, "Can not disable SSLv2 hello compatibility");
427 nss_error("upscli_init / SSL_OptionSetDefault(SSL_V2_COMPATIBLE_HELLO)");
428 return -1;
429 }
430 if (certname) {
431 nsscertname = xstrdup(certname);
432 }
433 if (certpasswd) {
434 nsscertpasswd = xstrdup(certpasswd);
435 }
436 verify_certificate = certverify;
437 #else
438 /* Note: historically we do not return with error here,
439 * just fall through to below and treat as initialized.
440 */
441 upslogx(LOG_ERR, "upscli_init called but SSL wasn't compiled in");
442 #endif /* WITH_OPENSSL | WITH_NSS */
443
444 upscli_initialized = 1;
445 return 1;
446 }
447
upscli_add_host_cert(const char * hostname,const char * certname,int certverify,int forcessl)448 void upscli_add_host_cert(const char* hostname, const char* certname, int certverify, int forcessl)
449 {
450 #ifdef WITH_NSS
451 HOST_CERT_t* cert = xmalloc(sizeof(HOST_CERT_t));
452 cert->next = first_host_cert;
453 cert->host = xstrdup(hostname);
454 cert->certname = xstrdup(certname);
455 cert->certverify = certverify;
456 cert->forcessl = forcessl;
457 first_host_cert = cert;
458 #else
459 NUT_UNUSED_VARIABLE(hostname);
460 NUT_UNUSED_VARIABLE(certname);
461 NUT_UNUSED_VARIABLE(certverify);
462 NUT_UNUSED_VARIABLE(forcessl);
463 #endif /* WITH_NSS */
464 }
465
upscli_find_host_cert(const char * hostname)466 static HOST_CERT_t* upscli_find_host_cert(const char* hostname)
467 {
468 #ifdef WITH_NSS
469 HOST_CERT_t* cert = first_host_cert;
470 if (hostname != NULL) {
471 while (cert != NULL) {
472 if (cert->host != NULL && strcmp(cert->host, hostname)==0 ) {
473 return cert;
474 }
475 cert = cert->next;
476 }
477 }
478 #else
479 NUT_UNUSED_VARIABLE(hostname);
480 #endif /* WITH_NSS */
481 return NULL;
482 }
483
upscli_cleanup(void)484 int upscli_cleanup(void)
485 {
486 #ifdef WITH_OPENSSL
487 if (ssl_ctx) {
488 SSL_CTX_free(ssl_ctx);
489 ssl_ctx = NULL;
490 }
491
492 #endif /* WITH_OPENSSL */
493
494 #ifdef WITH_NSS
495 /* Called to force cache clearing to prevent NSS shutdown failures.
496 * http://www.mozilla.org/projects/security/pki/nss/ref/ssl/sslfnc.html#1138601
497 */
498 SSL_ClearSessionCache();
499 NSS_Shutdown();
500 PR_Cleanup();
501 /* Called to release memory arena used by NSS/NSPR.
502 * Prevent to show all PL_ArenaAllocate mem alloc as leaks.
503 * https://developer.mozilla.org/en/NSS_Memory_allocation
504 */
505 PL_ArenaFinish();
506 #endif /* WITH_NSS */
507
508 upscli_initialized = 0;
509 return 1;
510 }
511
upscli_strerror(UPSCONN_t * ups)512 const char *upscli_strerror(UPSCONN_t *ups)
513 {
514 #ifdef WITH_OPENSSL
515 unsigned long err;
516 char sslbuf[UPSCLI_ERRBUF_LEN];
517 #endif
518
519 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
520 #pragma GCC diagnostic push
521 #endif
522 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
523 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
524 #endif
525 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY
526 #pragma GCC diagnostic ignored "-Wformat-security"
527 #endif
528
529 if (!ups) {
530 return upscli_errlist[UPSCLI_ERR_INVALIDARG].str;
531 }
532
533 if (ups->upsclient_magic != UPSCLIENT_MAGIC) {
534 return upscli_errlist[UPSCLI_ERR_INVALIDARG].str;
535 }
536
537 if (ups->upserror > UPSCLI_ERR_MAX) {
538 return "Invalid error number";
539 }
540
541 switch (upscli_errlist[ups->upserror].flags) {
542
543 case 0: /* simple error */
544 return upscli_errlist[ups->upserror].str;
545
546 case 1: /* add message from system's strerror */
547 snprintf(ups->errbuf, UPSCLI_ERRBUF_LEN,
548 upscli_errlist[ups->upserror].str,
549 strerror(ups->syserrno));
550 return ups->errbuf;
551
552 case 2: /* SSL error */
553 #ifdef WITH_OPENSSL
554 err = ERR_get_error();
555 if (err) {
556 ERR_error_string(err, sslbuf);
557 snprintf(ups->errbuf, UPSCLI_ERRBUF_LEN,
558 upscli_errlist[ups->upserror].str,
559 sslbuf);
560 } else {
561 snprintf(ups->errbuf, UPSCLI_ERRBUF_LEN,
562 upscli_errlist[ups->upserror].str,
563 "peer disconnected");
564 }
565 #elif defined(WITH_NSS) /* WITH_OPENSSL */
566 if (PR_GetErrorTextLength() < UPSCLI_ERRBUF_LEN) {
567 PR_GetErrorText(ups->errbuf);
568 } else {
569 snprintf(ups->errbuf, UPSCLI_ERRBUF_LEN,
570 "SSL error #%ld, message too long to be displayed",
571 (long)PR_GetError());
572 }
573 #else
574 snprintf(ups->errbuf, UPSCLI_ERRBUF_LEN,
575 "SSL error, but SSL wasn't enabled at compile-time");
576 #endif /* WITH_OPENSSL | WITH_NSS */
577 return ups->errbuf;
578
579 case 3: /* parsing (parseconf) error */
580 snprintf(ups->errbuf, UPSCLI_ERRBUF_LEN,
581 upscli_errlist[ups->upserror].str,
582 ups->pc_ctx.errmsg);
583 return ups->errbuf;
584 }
585
586 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
587 #pragma GCC diagnostic pop
588 #endif
589
590 /* fallthrough */
591
592 snprintf(ups->errbuf, UPSCLI_ERRBUF_LEN, "Unknown error flag %d",
593 upscli_errlist[ups->upserror].flags);
594
595 return ups->errbuf;
596 }
597
598 /* Read up to buflen bytes from fd and return the number of bytes
599 read. If no data is available within d_sec + d_usec, return 0.
600 On error, a value < 0 is returned (errno indicates error). */
upscli_select_read(const int fd,void * buf,const size_t buflen,const time_t d_sec,const suseconds_t d_usec)601 static ssize_t upscli_select_read(const int fd, void *buf, const size_t buflen, const time_t d_sec, const suseconds_t d_usec)
602 {
603 ssize_t ret;
604 fd_set fds;
605 struct timeval tv;
606
607 FD_ZERO(&fds);
608 FD_SET(fd, &fds);
609
610 tv.tv_sec = d_sec;
611 tv.tv_usec = d_usec;
612
613 ret = select(fd + 1, &fds, NULL, NULL, &tv);
614
615 if (ret < 1) {
616 return ret;
617 }
618
619 return read(fd, buf, buflen);
620 }
621
622 #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) )
623 # pragma GCC diagnostic push
624 #endif
625 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC
626 # pragma GCC diagnostic ignored "-Wtype-limits"
627 #endif
628 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC
629 # pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare"
630 #endif
631 /* internal: abstract the SSL calls for the other functions */
net_read(UPSCONN_t * ups,char * buf,size_t buflen,const long timeout)632 static ssize_t net_read(UPSCONN_t *ups, char *buf, size_t buflen, const long timeout)
633 {
634 ssize_t ret = -1;
635
636 #ifdef WITH_SSL
637 if (ups->ssl) {
638 #ifdef WITH_OPENSSL
639 /* SSL_* routines deal with int type for return and buflen
640 * We might need to window our I/O if we exceed 2GB (in
641 * 32-bit builds)... Not likely to exceed in 64-bit builds,
642 * but smaller systems with 16-bits might be endangered :)
643 */
644 assert(buflen <= INT_MAX);
645 int iret = SSL_read(ups->ssl, buf, (int)buflen);
646 assert(iret <= SSIZE_MAX);
647 ret = (ssize_t)iret;
648 #elif defined(WITH_NSS) /* WITH_OPENSSL */
649 /* PR_* routines deal in PRInt32 type
650 * We might need to window our I/O if we exceed 2GB :) */
651 assert(buflen <= PR_INT32_MAX);
652 ret = PR_Read(ups->ssl, buf, (PRInt32)buflen);
653 #endif /* WITH_OPENSSL | WITH_NSS*/
654
655 if (ret < 1) {
656 ups->upserror = UPSCLI_ERR_SSLERR;
657 }
658
659 return ret;
660 }
661 #endif
662
663 ret = upscli_select_read(ups->fd, buf, buflen, timeout, 0);
664
665 /* error reading data, server disconnected? */
666 if (ret < 0) {
667 ups->upserror = UPSCLI_ERR_READ;
668 ups->syserrno = errno;
669 }
670
671 /* no data available, server disconnected? */
672 if (ret == 0) {
673 ups->upserror = UPSCLI_ERR_SRVDISC;
674 }
675
676 return ret;
677 }
678 #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) )
679 # pragma GCC diagnostic pop
680 #endif
681
682 /* Write up to buflen bytes to fd and return the number of bytes
683 written. If no data is available within d_sec + d_usec, return 0.
684 On error, a value < 0 is returned (errno indicates error). */
upscli_select_write(const int fd,const void * buf,const size_t buflen,const time_t d_sec,const suseconds_t d_usec)685 static ssize_t upscli_select_write(const int fd, const void *buf, const size_t buflen, const time_t d_sec, const suseconds_t d_usec)
686 {
687 ssize_t ret;
688 fd_set fds;
689 struct timeval tv;
690
691 FD_ZERO(&fds);
692 FD_SET(fd, &fds);
693
694 tv.tv_sec = d_sec;
695 tv.tv_usec = d_usec;
696
697 ret = select(fd + 1, NULL, &fds, NULL, &tv);
698
699 if (ret < 1) {
700 return ret;
701 }
702
703 return write(fd, buf, buflen);
704 }
705
706 #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) )
707 # pragma GCC diagnostic push
708 #endif
709 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC
710 # pragma GCC diagnostic ignored "-Wtype-limits"
711 #endif
712 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC
713 # pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare"
714 #endif
715 /* internal: abstract the SSL calls for the other functions */
net_write(UPSCONN_t * ups,const char * buf,size_t buflen,const long timeout)716 static ssize_t net_write(UPSCONN_t *ups, const char *buf, size_t buflen, const long timeout)
717 {
718 ssize_t ret = -1;
719
720 #ifdef WITH_SSL
721 if (ups->ssl) {
722 #ifdef WITH_OPENSSL
723 /* SSL_* routines deal with int type for return and buflen
724 * We might need to window our I/O if we exceed 2GB (in
725 * 32-bit builds)... Not likely to exceed in 64-bit builds,
726 * but smaller systems with 16-bits might be endangered :)
727 */
728 assert(buflen <= INT_MAX);
729 int iret = SSL_write(ups->ssl, buf, (int)buflen);
730 assert(iret <= SSIZE_MAX);
731 ret = (ssize_t)iret;
732 #elif defined(WITH_NSS) /* WITH_OPENSSL */
733 /* PR_* routines deal in PRInt32 type
734 * We might need to window our I/O if we exceed 2GB :) */
735 assert(buflen <= PR_INT32_MAX);
736 ret = PR_Write(ups->ssl, buf, (PRInt32)buflen);
737 #endif /* WITH_OPENSSL | WITH_NSS */
738
739 if (ret < 1) {
740 ups->upserror = UPSCLI_ERR_SSLERR;
741 }
742
743 return ret;
744 }
745 #endif
746
747 ret = upscli_select_write(ups->fd, buf, buflen, timeout, 0);
748
749 /* error writing data, server disconnected? */
750 if (ret < 0) {
751 ups->upserror = UPSCLI_ERR_WRITE;
752 ups->syserrno = errno;
753 }
754
755 /* not ready for writing, server disconnected? */
756 if (ret == 0) {
757 ups->upserror = UPSCLI_ERR_SRVDISC;
758 }
759
760 return ret;
761 }
762 #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) )
763 # pragma GCC diagnostic pop
764 #endif
765
766
767 #ifdef WITH_SSL
768
769 /*
770 * 1 : OK
771 * -1 : ERROR
772 * 0 : SSL NOT SUPPORTED
773 */
upscli_sslinit(UPSCONN_t * ups,int verifycert)774 static int upscli_sslinit(UPSCONN_t *ups, int verifycert)
775 {
776 #ifdef WITH_OPENSSL
777 int res;
778 #elif defined(WITH_NSS) /* WITH_OPENSSL */
779 SECStatus status;
780 PRFileDesc *socket;
781 HOST_CERT_t *cert;
782 #endif /* WITH_OPENSSL | WITH_NSS */
783 char buf[UPSCLI_NETBUF_LEN];
784
785 /* Intend to initialize upscli with no ssl db if not already done.
786 * Compatibility stuff for old clients which do not initialize them.
787 */
788 if (upscli_initialized==0) {
789 upsdebugx(3, "upscli not initialized, "
790 "force initialisation without SSL configuration");
791 upscli_init(0, NULL, NULL, NULL);
792 }
793
794 /* see if upsd even talks SSL/TLS */
795 snprintf(buf, sizeof(buf), "STARTTLS\n");
796
797 if (upscli_sendline(ups, buf, strlen(buf)) != 0) {
798 return -1;
799 }
800
801 if (upscli_readline(ups, buf, sizeof(buf)) != 0) {
802 return -1;
803 }
804
805 if (strncmp(buf, "OK STARTTLS", 11) != 0) {
806 return 0; /* not supported */
807 }
808
809 /* upsd is happy, so let's crank up the client */
810
811 #ifdef WITH_OPENSSL
812
813 if (!ssl_ctx) {
814 upsdebugx(3, "SSL context is not available");
815 return 0;
816 }
817
818 ups->ssl = SSL_new(ssl_ctx);
819 if (!ups->ssl) {
820 upsdebugx(3, "Can not create SSL socket");
821 return 0;
822 }
823
824 if (SSL_set_fd(ups->ssl, ups->fd) != 1) {
825 upsdebugx(3, "Can not bind file descriptor to SSL socket");
826 return -1;
827 }
828
829 if (verifycert != 0) {
830 SSL_set_verify(ups->ssl, SSL_VERIFY_PEER, NULL);
831 } else {
832 SSL_set_verify(ups->ssl, SSL_VERIFY_NONE, NULL);
833 }
834
835 res = SSL_connect(ups->ssl);
836 switch(res)
837 {
838 case 1:
839 upsdebugx(3, "SSL connected (%s)", SSL_get_version(ups->ssl));
840 break;
841 case 0:
842 upslog_with_errno(1, "SSL_connect do not accept handshake.");
843 ssl_error(ups->ssl, res);
844 return -1;
845 default:
846 upslog_with_errno(1, "Unknown return value from SSL_connect %d", res);
847 ssl_error(ups->ssl, res);
848 return -1;
849 }
850
851 return 1;
852
853 #elif defined(WITH_NSS) /* WITH_OPENSSL */
854
855 socket = PR_ImportTCPSocket(ups->fd);
856 if (socket == NULL){
857 nss_error("upscli_sslinit / PR_ImportTCPSocket");
858 return -1;
859 }
860
861 ups->ssl = SSL_ImportFD(NULL, socket);
862 if (ups->ssl == NULL){
863 nss_error("upscli_sslinit / SSL_ImportFD");
864 return -1;
865 }
866
867 if (SSL_SetPKCS11PinArg(ups->ssl, ups) == -1){
868 nss_error("upscli_sslinit / SSL_SetPKCS11PinArg");
869 return -1;
870 }
871
872 if (verifycert) {
873 status = SSL_AuthCertificateHook(ups->ssl,
874 (SSLAuthCertificate)AuthCertificate, CERT_GetDefaultCertDB());
875 } else {
876 status = SSL_AuthCertificateHook(ups->ssl,
877 (SSLAuthCertificate)AuthCertificateDontVerify, CERT_GetDefaultCertDB());
878 }
879 if (status != SECSuccess) {
880 nss_error("upscli_sslinit / SSL_AuthCertificateHook");
881 return -1;
882 }
883
884 status = SSL_BadCertHook(ups->ssl, (SSLBadCertHandler)BadCertHandler, ups);
885 if (status != SECSuccess) {
886 nss_error("upscli_sslinit / SSL_BadCertHook");
887 return -1;
888 }
889
890 status = SSL_GetClientAuthDataHook(ups->ssl, (SSLGetClientAuthData)GetClientAuthData, ups);
891 if (status != SECSuccess) {
892 nss_error("upscli_sslinit / SSL_GetClientAuthDataHook");
893 return -1;
894 }
895
896 status = SSL_HandshakeCallback(ups->ssl, (SSLHandshakeCallback)HandshakeCallback, ups);
897 if (status != SECSuccess) {
898 nss_error("upscli_sslinit / SSL_HandshakeCallback");
899 return -1;
900 }
901
902 cert = upscli_find_host_cert(ups->host);
903 if (cert != NULL && cert->certname != NULL) {
904 upslogx(LOG_INFO, "Connecting in SSL to '%s' and look at certificate called '%s'",
905 ups->host, cert->certname);
906 status = SSL_SetURL(ups->ssl, cert->certname);
907 } else {
908 upslogx(LOG_NOTICE, "Connecting in SSL to '%s' (no certificate name specified)", ups->host);
909 status = SSL_SetURL(ups->ssl, ups->host);
910 }
911 if (status != SECSuccess) {
912 nss_error("upscli_sslinit / SSL_SetURL");
913 return -1;
914 }
915
916 status = SSL_ResetHandshake(ups->ssl, PR_FALSE);
917 if (status != SECSuccess) {
918 nss_error("upscli_sslinit / SSL_ResetHandshake");
919 ups->ssl = NULL;
920 /* EKI wtf unimport or free the socket ? */
921 return -1;
922 }
923
924 status = SSL_ForceHandshake(ups->ssl);
925 if (status != SECSuccess) {
926 nss_error("upscli_sslinit / SSL_ForceHandshake");
927 ups->ssl = NULL;
928 /* EKI wtf unimport or free the socket ? */
929 /* TODO : Close the connection. */
930 return -1;
931 }
932
933 return 1;
934
935 #endif /* WITH_OPENSSL | WITH_NSS */
936 }
937
938 #else /* WITH_SSL */
939
upscli_sslinit(UPSCONN_t * ups,int verifycert)940 static int upscli_sslinit(UPSCONN_t *ups, int verifycert)
941 {
942 NUT_UNUSED_VARIABLE(ups);
943 NUT_UNUSED_VARIABLE(verifycert);
944
945 return 0; /* not supported */
946 }
947
948 #endif /* WITH_SSL */
949
upscli_tryconnect(UPSCONN_t * ups,const char * host,int port,int flags,struct timeval * timeout)950 int upscli_tryconnect(UPSCONN_t *ups, const char *host, int port, int flags,struct timeval * timeout)
951 {
952 int sock_fd;
953 struct addrinfo hints, *res, *ai;
954 char sport[NI_MAXSERV];
955 int v, certverify, tryssl, forcessl, ret;
956 HOST_CERT_t* hostcert;
957 fd_set wfds;
958 int error;
959 socklen_t error_size;
960 long fd_flags;
961
962 if (!ups) {
963 return -1;
964 }
965
966 /* clear out any lingering junk */
967 memset(ups, 0, sizeof(*ups));
968 ups->upsclient_magic = UPSCLIENT_MAGIC;
969 ups->fd = -1;
970
971 if (!host) {
972 ups->upserror = UPSCLI_ERR_NOSUCHHOST;
973 return -1;
974 }
975
976 snprintf(sport, sizeof(sport), "%hu", (unsigned short int)port);
977
978 memset(&hints, 0, sizeof(hints));
979
980 if (flags & UPSCLI_CONN_INET6) {
981 hints.ai_family = AF_INET6;
982 } else if (flags & UPSCLI_CONN_INET) {
983 hints.ai_family = AF_INET;
984 } else {
985 hints.ai_family = AF_UNSPEC;
986 }
987
988 hints.ai_socktype = SOCK_STREAM;
989 hints.ai_protocol = IPPROTO_TCP;
990
991 while ((v = getaddrinfo(host, sport, &hints, &res)) != 0) {
992 switch (v)
993 {
994 case EAI_AGAIN:
995 continue;
996 case EAI_NONAME:
997 ups->upserror = UPSCLI_ERR_NOSUCHHOST;
998 return -1;
999 case EAI_MEMORY:
1000 ups->upserror = UPSCLI_ERR_NOMEM;
1001 return -1;
1002 case EAI_SYSTEM:
1003 ups->syserrno = errno;
1004 break;
1005 }
1006
1007 ups->upserror = UPSCLI_ERR_UNKNOWN;
1008 return -1;
1009 }
1010
1011 for (ai = res; ai != NULL; ai = ai->ai_next) {
1012
1013 sock_fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
1014
1015 if (sock_fd < 0) {
1016 switch (errno)
1017 {
1018 case EAFNOSUPPORT:
1019 case EINVAL:
1020 break;
1021 default:
1022 ups->upserror = UPSCLI_ERR_SOCKFAILURE;
1023 ups->syserrno = errno;
1024 }
1025 continue;
1026 }
1027
1028 /* non blocking connect */
1029 if(timeout != NULL) {
1030 fd_flags = fcntl(sock_fd, F_GETFL);
1031 fd_flags |= O_NONBLOCK;
1032 fcntl(sock_fd, F_SETFL, fd_flags);
1033 }
1034
1035 while ((v = connect(sock_fd, ai->ai_addr, ai->ai_addrlen)) < 0) {
1036 if(errno == EINPROGRESS || SOLARIS_i386_NBCONNECT_ENOENT(errno) || AIX_NBCONNECT_0(errno)) {
1037 FD_ZERO(&wfds);
1038 FD_SET(sock_fd, &wfds);
1039 select(sock_fd+1,NULL,&wfds,NULL,
1040 timeout);
1041 if (FD_ISSET(sock_fd, &wfds)) {
1042 error_size = sizeof(error);
1043 getsockopt(sock_fd,SOL_SOCKET,SO_ERROR,
1044 &error,&error_size);
1045 if( error == 0) {
1046 /* connect successful */
1047 v = 0;
1048 break;
1049 }
1050 errno = error;
1051 }
1052 else {
1053 /* Timeout */
1054 v = -1;
1055 break;
1056 }
1057 }
1058
1059 switch (errno)
1060 {
1061 case EAFNOSUPPORT:
1062 break;
1063 case EINTR:
1064 case EAGAIN:
1065 continue;
1066 default:
1067 ups->upserror = UPSCLI_ERR_CONNFAILURE;
1068 ups->syserrno = errno;
1069 }
1070 break;
1071 }
1072
1073 if (v < 0) {
1074 close(sock_fd);
1075 continue;
1076 }
1077
1078 /* switch back to blocking operation */
1079 if(timeout != NULL) {
1080 fd_flags = fcntl(sock_fd, F_GETFL);
1081 fd_flags &= ~O_NONBLOCK;
1082 fcntl(sock_fd, F_SETFL, fd_flags);
1083 }
1084
1085 ups->fd = sock_fd;
1086 ups->upserror = 0;
1087 ups->syserrno = 0;
1088 break;
1089 }
1090
1091 freeaddrinfo(res);
1092
1093 if (ups->fd < 0) {
1094 return -1;
1095 }
1096
1097 pconf_init(&ups->pc_ctx, NULL);
1098
1099 ups->host = strdup(host);
1100
1101 if (!ups->host) {
1102 ups->upserror = UPSCLI_ERR_NOMEM;
1103 upscli_disconnect(ups);
1104 return -1;
1105 }
1106
1107 ups->port = port;
1108
1109 hostcert = upscli_find_host_cert(host);
1110
1111 if (hostcert != NULL) {
1112 /* An host security rule is specified. */
1113 certverify = hostcert->certverify;
1114 forcessl = hostcert->forcessl;
1115 } else {
1116 certverify = (flags & UPSCLI_CONN_CERTVERIF) != 0 ? 1 : 0;
1117 forcessl = (flags & UPSCLI_CONN_REQSSL) != 0 ? 1 : 0;
1118 }
1119 tryssl = (flags & UPSCLI_CONN_TRYSSL) != 0 ? 1 : 0;
1120
1121 if (tryssl || forcessl) {
1122 ret = upscli_sslinit(ups, certverify);
1123 if (forcessl && ret != 1) {
1124 upslogx(LOG_ERR, "Can not connect to %s in SSL, disconnect", host);
1125 ups->upserror = UPSCLI_ERR_SSLFAIL;
1126 upscli_disconnect(ups);
1127 return -1;
1128 } else if (tryssl && ret == -1) {
1129 upslogx(LOG_NOTICE, "Error while connecting to %s, disconnect", host);
1130 upscli_disconnect(ups);
1131 return -1;
1132 } else if (tryssl && ret == 0) {
1133 if (certverify != 0) {
1134 upslogx(LOG_NOTICE, "Can not connect to %s in SSL and "
1135 "certificate is needed, disconnect", host);
1136 upscli_disconnect(ups);
1137 return -1;
1138 }
1139 upsdebugx(3, "Can not connect to %s in SSL, continue unencrypted", host);
1140 } else {
1141 upslogx(LOG_INFO, "Connected to %s in SSL", host);
1142 if (certverify == 0) {
1143 /* you REALLY should set CERTVERIFY to 1 if using SSL... */
1144 upslogx(LOG_WARNING, "Certificate verification is disabled");
1145 }
1146 }
1147 }
1148
1149 return 0;
1150 }
1151
upscli_connect(UPSCONN_t * ups,const char * host,int port,int flags)1152 int upscli_connect(UPSCONN_t *ups, const char *host, int port, int flags)
1153 {
1154 return upscli_tryconnect(ups,host,port,flags,NULL);
1155 }
1156
1157 /* map upsd error strings back to upsclient internal numbers */
1158 static struct {
1159 int errnum;
1160 const char *text;
1161 } upsd_errlist[] =
1162 {
1163 { UPSCLI_ERR_VARNOTSUPP, "VAR-NOT-SUPPORTED" },
1164 { UPSCLI_ERR_UNKNOWNUPS, "UNKNOWN-UPS" },
1165 { UPSCLI_ERR_ACCESSDENIED, "ACCESS-DENIED" },
1166 { UPSCLI_ERR_PWDREQUIRED, "PASSWORD-REQUIRED" },
1167 { UPSCLI_ERR_PWDINCORRECT, "PASSWORD-INCORRECT" },
1168 { UPSCLI_ERR_MISSINGARG, "MISSING-ARGUMENT" },
1169 { UPSCLI_ERR_DATASTALE, "DATA-STALE" },
1170 { UPSCLI_ERR_VARUNKNOWN, "VAR-UNKNOWN" },
1171 { UPSCLI_ERR_LOGINTWICE, "ALREADY-LOGGED-IN" },
1172 { UPSCLI_ERR_PWDSETTWICE, "ALREADY-SET-PASSWORD" },
1173 { UPSCLI_ERR_UNKNOWNTYPE, "UNKNOWN-TYPE" },
1174 { UPSCLI_ERR_UNKNOWNVAR, "UNKNOWN-VAR" },
1175 { UPSCLI_ERR_VARREADONLY, "READONLY" },
1176 { UPSCLI_ERR_TOOLONG, "TOO-LONG" },
1177 { UPSCLI_ERR_INVALIDVALUE, "INVALID-VALUE" },
1178 { UPSCLI_ERR_SETFAILED, "SET-FAILED" },
1179 { UPSCLI_ERR_UNKINSTCMD, "UNKNOWN-INSTCMD" },
1180 { UPSCLI_ERR_CMDFAILED, "INSTCMD-FAILED" },
1181 { UPSCLI_ERR_CMDNOTSUPP, "CMD-NOT-SUPPORTED" },
1182 { UPSCLI_ERR_INVUSERNAME, "INVALID-USERNAME" },
1183 { UPSCLI_ERR_USERSETTWICE, "ALREADY-SET-USERNAME" },
1184 { UPSCLI_ERR_UNKCOMMAND, "UNKNOWN-COMMAND" },
1185 { UPSCLI_ERR_INVPASSWORD, "INVALID-PASSWORD" },
1186 { UPSCLI_ERR_USERREQUIRED, "USERNAME-REQUIRED" },
1187 { UPSCLI_ERR_DRVNOTCONN, "DRIVER-NOT-CONNECTED" },
1188
1189 { 0, NULL, }
1190 };
1191
upscli_errcheck(UPSCONN_t * ups,char * buf)1192 static int upscli_errcheck(UPSCONN_t *ups, char *buf)
1193 {
1194 int i;
1195
1196 if (!ups) {
1197 return -1;
1198 }
1199
1200 if (!buf) {
1201 ups->upserror = UPSCLI_ERR_INVALIDARG;
1202 return -1;
1203 }
1204
1205 /* see if it's even an error now */
1206 if (strncmp(buf, "ERR", 3) != 0) {
1207 return 0;
1208 }
1209
1210 /* look it up in the table */
1211 for (i = 0; upsd_errlist[i].text != NULL; i++) {
1212 if (!strncmp(&buf[4], upsd_errlist[i].text,
1213 strlen(upsd_errlist[i].text))) {
1214 ups->upserror = upsd_errlist[i].errnum;
1215 return -1;
1216 }
1217 }
1218
1219 /* hmm - don't know what upsd is telling us */
1220 ups->upserror = UPSCLI_ERR_UNKNOWN;
1221 return -1;
1222 }
1223
build_cmd(char * buf,size_t bufsize,const char * cmdname,size_t numarg,const char ** arg)1224 static void build_cmd(char *buf, size_t bufsize, const char *cmdname,
1225 size_t numarg, const char **arg)
1226 {
1227 size_t i;
1228 size_t len;
1229 char enc[UPSCLI_NETBUF_LEN];
1230 const char *format;
1231
1232 memset(buf, '\0', bufsize);
1233 snprintf(buf, bufsize, "%s", cmdname);
1234
1235 /* encode all arguments so they arrive intact */
1236 for (i = 0; i < numarg; i++) {
1237
1238 if (strchr(arg[i], ' ')) {
1239 format = " \"%s\""; /* wrap in "" */
1240 } else {
1241 format = " %s";
1242 }
1243
1244 /* snprintfcat would tie us to common */
1245
1246 len = strlen(buf);
1247 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
1248 #pragma GCC diagnostic push
1249 #endif
1250 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
1251 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
1252 #endif
1253 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY
1254 #pragma GCC diagnostic ignored "-Wformat-security"
1255 #endif
1256 snprintf(buf + len, bufsize - len, format,
1257 pconf_encode(arg[i], enc, sizeof(enc)));
1258 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
1259 #pragma GCC diagnostic pop
1260 #endif
1261 }
1262
1263 len = strlen(buf);
1264 snprintf(buf + len, bufsize - len, "\n");
1265 }
1266
1267 /* make sure upsd is giving us what we asked for */
verify_resp(size_t num,const char ** q,char ** a)1268 static int verify_resp(size_t num, const char **q, char **a)
1269 {
1270 size_t i;
1271
1272 for (i = 0; i < num; i++) {
1273 if (strcasecmp(q[i], a[i]) != 0) {
1274
1275 /* FUTURE: handle -/+ options here */
1276 return 0; /* mismatch */
1277 }
1278 }
1279
1280 return 1; /* OK */
1281 }
1282
upscli_get(UPSCONN_t * ups,size_t numq,const char ** query,size_t * numa,char *** answer)1283 int upscli_get(UPSCONN_t *ups, size_t numq, const char **query,
1284 size_t *numa, char ***answer)
1285 {
1286 char cmd[UPSCLI_NETBUF_LEN], tmp[UPSCLI_NETBUF_LEN];
1287
1288 if (!ups) {
1289 return -1;
1290 }
1291
1292 if (numq < 1) {
1293 ups->upserror = UPSCLI_ERR_INVALIDARG;
1294 return -1;
1295 }
1296
1297 /* create the string to send to upsd */
1298 build_cmd(cmd, sizeof(cmd), "GET", numq, query);
1299
1300 if (upscli_sendline(ups, cmd, strlen(cmd)) != 0) {
1301 return -1;
1302 }
1303
1304 if (upscli_readline(ups, tmp, sizeof(tmp)) != 0) {
1305 return -1;
1306 }
1307
1308 if (upscli_errcheck(ups, tmp) != 0) {
1309 return -1;
1310 }
1311
1312 if (!pconf_line(&ups->pc_ctx, tmp)) {
1313 ups->upserror = UPSCLI_ERR_PARSE;
1314 return -1;
1315 }
1316
1317 /* q: [GET] VAR <ups> <var> *
1318 * a: VAR <ups> <var> <val> */
1319
1320 if (ups->pc_ctx.numargs < numq) {
1321 ups->upserror = UPSCLI_ERR_PROTOCOL;
1322 return -1;
1323 }
1324
1325 if (!verify_resp(numq, query, ups->pc_ctx.arglist)) {
1326 ups->upserror = UPSCLI_ERR_PROTOCOL;
1327 return -1;
1328 }
1329
1330 *numa = ups->pc_ctx.numargs;
1331 *answer = ups->pc_ctx.arglist;
1332
1333 return 0;
1334 }
1335
upscli_list_start(UPSCONN_t * ups,size_t numq,const char ** query)1336 int upscli_list_start(UPSCONN_t *ups, size_t numq, const char **query)
1337 {
1338 char cmd[UPSCLI_NETBUF_LEN], tmp[UPSCLI_NETBUF_LEN];
1339
1340 if (!ups) {
1341 return -1;
1342 }
1343
1344 if (numq < 1) {
1345 ups->upserror = UPSCLI_ERR_INVALIDARG;
1346 return -1;
1347 }
1348
1349 /* create the string to send to upsd */
1350 build_cmd(cmd, sizeof(cmd), "LIST", numq, query);
1351
1352 if (upscli_sendline(ups, cmd, strlen(cmd)) != 0) {
1353 return -1;
1354 }
1355
1356 if (upscli_readline(ups, tmp, sizeof(tmp)) != 0) {
1357 return -1;
1358 }
1359
1360 if (upscli_errcheck(ups, tmp) != 0) {
1361 return -1;
1362 }
1363
1364 if (!pconf_line(&ups->pc_ctx, tmp)) {
1365 ups->upserror = UPSCLI_ERR_PARSE;
1366 return -1;
1367 }
1368
1369 if (ups->pc_ctx.numargs < 2) {
1370 ups->upserror = UPSCLI_ERR_PROTOCOL;
1371 return -1;
1372 }
1373
1374 /* the response must start with BEGIN LIST */
1375 if ((strcasecmp(ups->pc_ctx.arglist[0], "BEGIN") != 0) ||
1376 (strcasecmp(ups->pc_ctx.arglist[1], "LIST") != 0)) {
1377 ups->upserror = UPSCLI_ERR_PROTOCOL;
1378 return -1;
1379 }
1380
1381 /* q: [LIST] VAR <ups> *
1382 * a: [BEGIN LIST] VAR <ups> */
1383
1384 /* compare q[0]... to a[2]... */
1385
1386 if (!verify_resp(numq, query, &ups->pc_ctx.arglist[2])) {
1387 ups->upserror = UPSCLI_ERR_PROTOCOL;
1388 return -1;
1389 }
1390
1391 return 0;
1392 }
1393
upscli_list_next(UPSCONN_t * ups,size_t numq,const char ** query,size_t * numa,char *** answer)1394 int upscli_list_next(UPSCONN_t *ups, size_t numq, const char **query,
1395 size_t *numa, char ***answer)
1396 {
1397 char tmp[UPSCLI_NETBUF_LEN];
1398
1399 if (!ups) {
1400 return -1;
1401 }
1402
1403 if (upscli_readline(ups, tmp, sizeof(tmp)) != 0) {
1404 return -1;
1405 }
1406
1407 if (upscli_errcheck(ups, tmp) != 0) {
1408 return -1;
1409 }
1410
1411 if (!pconf_line(&ups->pc_ctx, tmp)) {
1412 ups->upserror = UPSCLI_ERR_PARSE;
1413 return -1;
1414 }
1415
1416 if (ups->pc_ctx.numargs < 1) {
1417 ups->upserror = UPSCLI_ERR_PROTOCOL;
1418 return -1;
1419 }
1420
1421 *numa = ups->pc_ctx.numargs;
1422 *answer = ups->pc_ctx.arglist;
1423
1424 /* see if this is the end */
1425 if (ups->pc_ctx.numargs >= 2) {
1426 if ((!strncmp(ups->pc_ctx.arglist[0], "END", 3)) &&
1427 (!strncmp(ups->pc_ctx.arglist[1], "LIST", 4)))
1428 return 0;
1429 }
1430
1431 /* q: VAR <ups> */
1432 /* a: VAR <ups> <val> */
1433
1434 if (!verify_resp(numq, query, ups->pc_ctx.arglist)) {
1435 ups->upserror = UPSCLI_ERR_PROTOCOL;
1436 return -1;
1437 }
1438
1439 /* just another part of the list */
1440 return 1;
1441 }
1442
upscli_sendline_timeout(UPSCONN_t * ups,const char * buf,size_t buflen,const long timeout)1443 ssize_t upscli_sendline_timeout(UPSCONN_t *ups, const char *buf, size_t buflen, const long timeout)
1444 {
1445 ssize_t ret;
1446
1447 if (!ups) {
1448 return -1;
1449 }
1450
1451 if (ups->fd < 0) {
1452 ups->upserror = UPSCLI_ERR_DRVNOTCONN;
1453 return -1;
1454 }
1455
1456 if ((!buf) || (buflen < 1)) {
1457 ups->upserror = UPSCLI_ERR_INVALIDARG;
1458 return -1;
1459 }
1460
1461 if (ups->upsclient_magic != UPSCLIENT_MAGIC) {
1462 ups->upserror = UPSCLI_ERR_INVALIDARG;
1463 return -1;
1464 }
1465
1466 ret = net_write(ups, buf, buflen, timeout);
1467
1468 if (ret < 1) {
1469 upscli_disconnect(ups);
1470 return -1;
1471 }
1472
1473 return 0;
1474 }
1475
upscli_sendline(UPSCONN_t * ups,const char * buf,size_t buflen)1476 ssize_t upscli_sendline(UPSCONN_t *ups, const char *buf, size_t buflen)
1477 {
1478 return upscli_sendline_timeout(ups, buf, buflen, 0);
1479 }
1480
upscli_readline_timeout(UPSCONN_t * ups,char * buf,size_t buflen,const long timeout)1481 ssize_t upscli_readline_timeout(UPSCONN_t *ups, char *buf, size_t buflen, const long timeout)
1482 {
1483 ssize_t ret;
1484 size_t recv;
1485
1486 if (!ups) {
1487 return -1;
1488 }
1489
1490 if (ups->fd < 0) {
1491 ups->upserror = UPSCLI_ERR_DRVNOTCONN;
1492 return -1;
1493 }
1494
1495 if ((!buf) || (buflen < 1)) {
1496 ups->upserror = UPSCLI_ERR_INVALIDARG;
1497 return -1;
1498 }
1499
1500 if (ups->upsclient_magic != UPSCLIENT_MAGIC) {
1501 ups->upserror = UPSCLI_ERR_INVALIDARG;
1502 return -1;
1503 }
1504
1505 for (recv = 0; recv < (buflen-1); recv++) {
1506
1507 if (ups->readidx == ups->readlen) {
1508
1509 ret = net_read(ups, ups->readbuf, sizeof(ups->readbuf), timeout);
1510
1511 if (ret < 1) {
1512 upscli_disconnect(ups);
1513 return -1;
1514 }
1515
1516 /* Here ret is safe to cast since it is >=1 and certainly
1517 * fits under SIZE_MAX being it signed sibling
1518 */
1519 ups->readlen = (size_t)ret;
1520 ups->readidx = 0;
1521 }
1522
1523 buf[recv] = ups->readbuf[ups->readidx++];
1524
1525 if (buf[recv] == '\n') {
1526 break;
1527 }
1528 }
1529
1530 buf[recv] = '\0';
1531 return 0;
1532 }
1533
upscli_readline(UPSCONN_t * ups,char * buf,size_t buflen)1534 ssize_t upscli_readline(UPSCONN_t *ups, char *buf, size_t buflen)
1535 {
1536 return upscli_readline_timeout(ups, buf, buflen, DEFAULT_NETWORK_TIMEOUT);
1537 }
1538
1539 /* split upsname[@hostname[:port]] into separate components */
upscli_splitname(const char * buf,char ** upsname,char ** hostname,int * port)1540 int upscli_splitname(const char *buf, char **upsname, char **hostname, int *port)
1541 {
1542 char *s, tmp[SMALLBUF], *last = NULL;
1543
1544 /* paranoia */
1545 if ((!buf) || (!upsname) || (!hostname) || (!port)) {
1546 return -1;
1547 }
1548
1549 if (snprintf(tmp, sizeof(tmp), "%s", buf) < 1) {
1550 fprintf(stderr, "upscli_splitname: can't parse empty string\n");
1551 return -1;
1552 }
1553
1554 s = strchr(tmp, '@');
1555
1556 if ((*upsname = strdup(strtok_r(tmp, "@", &last))) == NULL) {
1557 fprintf(stderr, "upscli_splitname: strdup failed\n");
1558 return -1;
1559 }
1560
1561 /* only a upsname is specified, fill in defaults */
1562 if (s == NULL) {
1563 if ((*hostname = strdup("localhost")) == NULL) {
1564 fprintf(stderr, "upscli_splitname: strdup failed\n");
1565 return -1;
1566 }
1567
1568 *port = PORT;
1569 return 0;
1570 }
1571
1572 return upscli_splitaddr(s+1, hostname, port);
1573 }
1574
1575 /* split hostname[:port] into separate components */
upscli_splitaddr(const char * buf,char ** hostname,int * port)1576 int upscli_splitaddr(const char *buf, char **hostname, int *port)
1577 {
1578 char *s, tmp[SMALLBUF], *last = NULL;
1579
1580 /* paranoia */
1581 if ((!buf) || (!hostname) || (!port)) {
1582 return -1;
1583 }
1584
1585 if (snprintf(tmp, sizeof(tmp), "%s", buf) < 1) {
1586 fprintf(stderr, "upscli_splitaddr: can't parse empty string\n");
1587 return -1;
1588 }
1589
1590 if (*tmp == '[') {
1591 if (strchr(tmp, ']') == NULL) {
1592 fprintf(stderr, "upscli_splitaddr: missing closing bracket in [domain literal]\n");
1593 return -1;
1594 }
1595
1596 if ((*hostname = strdup(strtok_r(tmp+1, "]", &last))) == NULL) {
1597 fprintf(stderr, "upscli_splitaddr: strdup failed\n");
1598 return -1;
1599 }
1600
1601 /* no port specified, use default */
1602 if (((s = strtok_r(NULL, "\0", &last)) == NULL) || (*s != ':')) {
1603 *port = PORT;
1604 return 0;
1605 }
1606 } else {
1607 s = strchr(tmp, ':');
1608
1609 if ((*hostname = strdup(strtok_r(tmp, ":", &last))) == NULL) {
1610 fprintf(stderr, "upscli_splitaddr: strdup failed\n");
1611 return -1;
1612 }
1613
1614 /* no port specified, use default */
1615 if (s == NULL) {
1616 *port = PORT;
1617 return 0;
1618 }
1619 }
1620
1621 /* FIXME: This assumes but does not check that "long" port
1622 * fits in an "int" and is in IP range (under 65535) */
1623 if ((*(++s) == '\0') || ((*port = (int)strtol(s, NULL, 10)) < 1 )) {
1624 fprintf(stderr, "upscli_splitaddr: no port specified after ':' separator\n");
1625 return -1;
1626 }
1627
1628 return 0;
1629 }
1630
upscli_disconnect(UPSCONN_t * ups)1631 int upscli_disconnect(UPSCONN_t *ups)
1632 {
1633 if (!ups) {
1634 return -1;
1635 }
1636
1637 if (ups->upsclient_magic != UPSCLIENT_MAGIC) {
1638 return -1;
1639 }
1640
1641 pconf_finish(&ups->pc_ctx);
1642
1643 free(ups->host);
1644 ups->host = NULL;
1645
1646 if (ups->fd < 0) {
1647 return 0;
1648 }
1649
1650 net_write(ups, "LOGOUT\n", 7, 0);
1651
1652 #ifdef WITH_OPENSSL
1653 if (ups->ssl) {
1654 SSL_shutdown(ups->ssl);
1655 SSL_free(ups->ssl);
1656 ups->ssl = NULL;
1657 }
1658 #elif defined(WITH_NSS) /* WITH_OPENSSL */
1659 if (ups->ssl) {
1660 PR_Shutdown(ups->ssl, PR_SHUTDOWN_BOTH);
1661 PR_Close(ups->ssl);
1662 ups->ssl = NULL;
1663 }
1664 #endif /* WITH_OPENSSL | WITH_NSS */
1665
1666 shutdown(ups->fd, shutdown_how);
1667
1668 close(ups->fd);
1669 ups->fd = -1;
1670
1671 return 0;
1672 }
1673
upscli_fd(UPSCONN_t * ups)1674 int upscli_fd(UPSCONN_t *ups)
1675 {
1676 if (!ups) {
1677 return -1;
1678 }
1679
1680 if (ups->upsclient_magic != UPSCLIENT_MAGIC) {
1681 return -1;
1682 }
1683
1684 return ups->fd;
1685 }
1686
upscli_upserror(UPSCONN_t * ups)1687 int upscli_upserror(UPSCONN_t *ups)
1688 {
1689 if (!ups) {
1690 return -1;
1691 }
1692
1693 if (ups->upsclient_magic != UPSCLIENT_MAGIC) {
1694 return -1;
1695 }
1696
1697 return ups->upserror;
1698 }
1699
upscli_ssl(UPSCONN_t * ups)1700 int upscli_ssl(UPSCONN_t *ups)
1701 {
1702 if (!ups) {
1703 return -1;
1704 }
1705
1706 if (ups->upsclient_magic != UPSCLIENT_MAGIC) {
1707 return -1;
1708 }
1709
1710 #ifdef WITH_SSL
1711 if (ups->ssl) {
1712 return 1;
1713 }
1714 #endif /* WITH_SSL */
1715
1716 return 0;
1717 }
1718