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