1 /*****************************************************************************
2  *  Contributed by Levi Pearson <lpearson@lnxi.com>.
3  *
4  *  Written by Chris Dunlap <cdunlap@llnl.gov>.
5  *  Copyright (C) 2007-2018 Lawrence Livermore National Security, LLC.
6  *  Copyright (C) 2001-2007 The Regents of the University of California.
7  *  UCRL-CODE-2002-009.
8  *
9  *  This file is part of ConMan: The Console Manager.
10  *  For details, see <https://dun.github.io/conman/>.
11  *
12  *  ConMan is free software: you can redistribute it and/or modify it under
13  *  the terms of the GNU General Public License as published by the Free
14  *  Software Foundation, either version 3 of the License, or (at your option)
15  *  any later version.
16  *
17  *  ConMan is distributed in the hope that it will be useful, but WITHOUT
18  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20  *  for more details.
21  *
22  *  You should have received a copy of the GNU General Public License along
23  *  with ConMan.  If not, see <http://www.gnu.org/licenses/>.
24  *****************************************************************************/
25 
26 
27 #if HAVE_CONFIG_H
28 #  include <config.h>
29 #endif /* HAVE_CONFIG_H */
30 
31 #if HAVE_IPMICONSOLE_H
32 #  include <ipmiconsole.h>
33 #endif /* HAVE_IPMICONSOLE_H */
34 
35 #include <assert.h>
36 #include <ctype.h>
37 #include <errno.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include "common.h"
43 #include "list.h"
44 #include "log.h"
45 #include "server.h"
46 #include "tpoll.h"
47 #include "util.h"
48 #include "util-file.h"
49 #include "util-str.h"
50 #include "wrapper.h"
51 
52 
53 static int parse_ipmi_opts_v1(
54     ipmiopt_t *iopts, char *str, char *errbuf, int errlen);
55 static int process_ipmi_opt(
56     ipmiopt_t *iopts, const char *str, char *errbuf, int errlen);
57 static int is_ipmi_opt_tag(const char *str);
58 static int process_ipmi_opt_username(
59     ipmiopt_t *iopts, const char *str, char *errbuf, int errlen);
60 static int process_ipmi_opt_password(
61     ipmiopt_t *iopts, const char *str, char *errbuf, int errlen);
62 static int process_ipmi_opt_k_g(
63     ipmiopt_t *iopts, const char *str, char *errbuf, int errlen);
64 static int process_ipmi_opt_privilege(
65     ipmiopt_t *iopts, const char *str, char *errbuf, int errlen);
66 static int process_ipmi_opt_cipher(
67     ipmiopt_t *iopts, const char *str, char *errbuf, int errlen);
68 static int process_ipmi_opt_workaround(
69     ipmiopt_t *iopts, const char *str, char *errbuf, int errlen);
70 static int parse_key(char *dst, const char *src, size_t dstlen);
71 static void disconnect_ipmi_obj(obj_t *ipmi);
72 static int connect_ipmi_obj(obj_t *ipmi);
73 static int initiate_ipmi_connect(obj_t *ipmi);
74 static int create_ipmi_ctx(obj_t *ipmi);
75 static int complete_ipmi_connect(obj_t *ipmi);
76 static void fail_ipmi_connect(obj_t *ipmi);
77 static void reset_ipmi_delay(obj_t *ipmi);
78 
79 extern tpoll_t tp_global;               /* defined in server.c */
80 static int is_ipmi_engine_started = 0;
81 
82 
ipmi_init(int num_consoles)83 void ipmi_init(int num_consoles)
84 {
85 /*  Starts the ipmiconsole engine to handle 'num_consoles' IPMI SOL consoles.
86  */
87     int num_threads;
88 
89     if (num_consoles <= 0) {
90         return;
91     }
92     if (is_ipmi_engine_started) {
93         return;
94     }
95     num_threads = ((num_consoles - 1) / IPMI_ENGINE_CONSOLES_PER_THREAD) + 1;
96     num_threads = MIN(num_threads, IPMICONSOLE_THREAD_COUNT_MAX);
97 
98     if (ipmiconsole_engine_init(num_threads, 0) < 0) {
99         log_err(0, "Unable to start IPMI SOL engine");
100     }
101     else {
102         log_msg(LOG_INFO,
103             "IPMI SOL engine started with %d thread%s for %d console%s",
104             num_threads, (num_threads == 1) ? "" : "s",
105             num_consoles, (num_consoles == 1) ? "" : "s");
106     }
107     is_ipmi_engine_started = 1;
108     return;
109 }
110 
111 
ipmi_fini(void)112 void ipmi_fini(void)
113 {
114 /*  Stops the ipmiconsole engine.
115  */
116     /*  Setting do_sol_session_cleanup to nonzero will cause
117      *    ipmiconsole_engine_teardown() to block until all active
118      *    IPMI SOL sessions have been cleanly closed or timed-out.
119      */
120     int do_sol_session_cleanup = 1;
121 
122     if (!is_ipmi_engine_started) {
123         return;
124     }
125     ipmiconsole_engine_teardown(do_sol_session_cleanup);
126     is_ipmi_engine_started = 0;
127     return;
128 }
129 
130 
is_ipmi_dev(const char * dev,char ** host_ref)131 int is_ipmi_dev(const char *dev, char **host_ref)
132 {
133 /*  Returns 1 if 'dev' appears to be a valid IPMI device name
134  *    (ie, contains the "ipmi" tag), storing a new string containing
135  *    the hostname in the reference parm 'host_ref'; o/w, returns 0.
136  */
137     const char * const prefix = "ipmi:";
138 
139     if (dev == NULL) {
140         return(0);
141     }
142     if (strncasecmp(dev, prefix, strlen(prefix)) != 0) {
143         return(0);
144     }
145     dev += strlen(prefix);
146     if (dev[0] == '\0') {
147         return(0);
148     }
149     if (host_ref) {
150         *host_ref = create_string(dev);
151     }
152     return(1);
153 }
154 
155 
init_ipmi_opts(ipmiopt_t * iopts)156 int init_ipmi_opts(ipmiopt_t *iopts)
157 {
158 /*  Initializes 'iopts' to the default values.
159  *  Returns 0 on success, -1 on error.
160  */
161     if (iopts == NULL) {
162         return(-1);
163     }
164     memset(iopts, 0, sizeof(ipmiopt_t));
165     iopts->privilegeLevel = -1;
166     iopts->cipherSuite = -1;
167     iopts->workaroundFlags = IPMICONSOLE_WORKAROUND_DEFAULT;
168     return(0);
169 }
170 
171 
parse_ipmi_opts(ipmiopt_t * iopts,const char * str,char * errbuf,int errlen)172 int parse_ipmi_opts(
173     ipmiopt_t *iopts, const char *str, char *errbuf, int errlen)
174 {
175 /*  Parses string 'str' for IPMI device options 'iopts'.
176  *    The string 'str' is broken up into comma-delimited tokens; as such,
177  *    token values for a given IPMI device option cannot contain commas.
178  *    The 'iopts' should be initialized to a default value beforehand.
179  *  Returns 0 and updates the 'iopts' struct on success; o/w, returns -1
180  *    (writing an error message into buffer 'errbuf' of length 'errlen').
181  */
182     ipmiopt_t           ioptsTmp;
183     char                buf[MAX_LINE];
184     char               *tok;
185     const char * const  separators = ",";
186 
187     if (iopts == NULL) {
188         log_err(0, "parse_ipmi_opts: iopts ptr is NULL");
189     }
190     ioptsTmp = *iopts;
191 
192     if (strlcpy(buf, str, sizeof(buf)) >= sizeof(buf)) {
193         if ((errbuf != NULL) && (errlen > 0)) {
194             snprintf(errbuf, errlen,
195                 "ipmiopts string exceeds %lu-byte maximum",
196                 (unsigned long) sizeof(buf) - 1);
197         }
198         return(-1);
199     }
200     /*  Support previous ipmiopts format for backwards-compatibility.
201      *  This behavior is considered deprecated and may be removed at any time.
202      */
203     if ((buf[0] != '\0') && (buf[1] != ':')) {
204         if (parse_ipmi_opts_v1(&ioptsTmp, buf, errbuf, errlen) < 0) {
205             return(-1);
206         }
207     }
208     else {
209         tok = strtok(buf, separators);
210         while (tok != NULL) {
211             if (process_ipmi_opt(&ioptsTmp, tok, errbuf, errlen) < 0) {
212                 return(-1);
213             }
214             tok = strtok(NULL, separators);
215         }
216     }
217     *iopts = ioptsTmp;
218     return(0);
219 }
220 
221 
parse_ipmi_opts_v1(ipmiopt_t * iopts,char * str,char * errbuf,int errlen)222 static int parse_ipmi_opts_v1(
223     ipmiopt_t *iopts, char *str, char *errbuf, int errlen)
224 {
225 /*  Parses/modifies string 'str' for IPMI device options.
226  *    The string 'str' is of the form "[<user>[,<pswd[,<K_g>[,<w-flag>]*]]]".
227  *    An empty 'str' is valid and denotes specifying the default behavior.
228  *    A "-" may be used to specify the IPMI default for a given option.
229  *  Returns 0 and updates the 'iopts' struct on success; o/w, returns -1
230  *    (writing an error message into buffer 'errbuf' of length 'errlen').
231  *  This behavior is considered deprecated and may be removed at any time.
232  */
233     char               *tok;
234     const char * const  separators = ",";
235 
236     assert(iopts != NULL);
237     assert(str != NULL);
238 
239     if ((tok = strtok(str, separators))) {
240         if ((tok[0] == '-') && (tok[1] == '\0')) {
241             tok++;
242         }
243         if (process_ipmi_opt_username(iopts, tok, errbuf, errlen) < 0) {
244             return(-1);
245         }
246     }
247     if ((tok = strtok(NULL, separators))) {
248         if ((tok[0] == '-') && (tok[1] == '\0')) {
249             tok++;
250         }
251         if (process_ipmi_opt_password(iopts, tok, errbuf, errlen) < 0) {
252             return(-1);
253         }
254     }
255     if ((tok = strtok(NULL, separators))) {
256         if ((tok[0] == '-') && (tok[1] == '\0')) {
257             tok++;
258         }
259         if (process_ipmi_opt_k_g(iopts, tok, errbuf, errlen) < 0) {
260             return(-1);
261         }
262     }
263     while ((tok = strtok(NULL, separators))) {
264         if ((tok[0] == '-') && (tok[1] == '\0')) {
265             tok++;
266         }
267         if (process_ipmi_opt_workaround(iopts, tok, errbuf, errlen) < 0) {
268             return(-1);
269         }
270     }
271     return(0);
272 }
273 
274 
process_ipmi_opt(ipmiopt_t * iopts,const char * str,char * errbuf,int errlen)275 static int process_ipmi_opt(
276     ipmiopt_t *iopts, const char *str, char *errbuf, int errlen)
277 {
278 /*  Parses string 'str' for a single IPMI device option.
279  *    The string 'str' is of the form "X:VALUE", where "X" is a single-char key
280  *    tag specifying the option type and "VALUE" is its corresponding value.
281  *  Returns 0 and updates the 'iopts' struct on success; o/w, returns -1
282  *    (writing an error message into buffer 'errbuf' of length 'errlen').
283  */
284     char        c;
285     const char *p;
286     int         rv = -1;
287 
288     assert(iopts != NULL);
289     assert(str != NULL);
290 
291     if (!is_ipmi_opt_tag(str)) {
292         if ((errbuf != NULL) && (errlen > 0)) {
293             snprintf(errbuf, errlen, "invalid ipmiopts string \"%s\"", str);
294         }
295         return(-1);
296     }
297     c = toupper((int) str[0]);
298     p = str + 2;
299     switch (c) {
300         case 'U':
301             rv = process_ipmi_opt_username(iopts, p, errbuf, errlen);
302             break;
303         case 'P':
304             rv = process_ipmi_opt_password(iopts, p, errbuf, errlen);
305             break;
306         case 'K':
307             rv = process_ipmi_opt_k_g(iopts, p, errbuf, errlen);
308             break;
309         case 'L':
310             rv = process_ipmi_opt_privilege(iopts, p, errbuf, errlen);
311             break;
312         case 'C':
313             rv = process_ipmi_opt_cipher(iopts, p, errbuf, errlen);
314             break;
315         case 'W':
316             rv = process_ipmi_opt_workaround(iopts, p, errbuf, errlen);
317             break;
318         default:
319             /*  This case should never happen since is_ipmi_opt_tag() above
320              *    has already validated the option tag.
321              */
322             log_err(0, "invalid ipmiopts tag '%c'", c);
323             break;
324     }
325     return((rv == 0) ? 0 : -1);
326 }
327 
328 
is_ipmi_opt_tag(const char * str)329 static int is_ipmi_opt_tag(const char *str)
330 {
331 /*  Returns 1 if string 'str' is a recognized ipmiopts tag; o/w, returns 0.
332  */
333     if ((str == NULL) || (str[0] == '\0') || (str[1] != ':')) {
334         return(0);
335     }
336     switch (toupper((int) str[0])) {
337         case 'U': case 'P': case 'K': case 'L': case 'C': case 'W':
338             return(1);
339     }
340     return(0);
341 }
342 
343 
process_ipmi_opt_username(ipmiopt_t * iopts,const char * str,char * errbuf,int errlen)344 static int process_ipmi_opt_username(
345     ipmiopt_t *iopts, const char *str, char *errbuf, int errlen)
346 {
347 /*  Parses string 'str' for the IPMI device username.
348  *    If the option value is the empty string, the IPMI default will be used.
349  *  Returns 0 and updates the 'iopts' struct on success; o/w, returns -1
350  *    (writing an error message into buffer 'errbuf' of length 'errlen').
351  */
352     assert(iopts != NULL);
353     assert(str != NULL);
354 
355     if (str[0] == '\0') {
356         iopts->username[0] = '\0';
357     }
358     else {
359         int n;
360 
361         n = strlcpy(iopts->username, str, sizeof(iopts->username));
362 
363         if ((size_t) n >= sizeof(iopts->username)) {
364             if ((errbuf != NULL) && (errlen > 0)) {
365                 snprintf(errbuf, errlen,
366                     "IPMI username exceeds %d-byte maximum",
367                     IPMI_MAX_USER_LEN);
368             }
369             return(-1);
370         }
371         if (!ipmiconsole_username_is_valid(iopts->username)) {
372             if ((errbuf != NULL) && (errlen > 0)) {
373                 snprintf(errbuf, errlen, "invalid IPMI username");
374             }
375             return(-1);
376         }
377     }
378     return(0);
379 }
380 
381 
process_ipmi_opt_password(ipmiopt_t * iopts,const char * str,char * errbuf,int errlen)382 static int process_ipmi_opt_password(
383     ipmiopt_t *iopts, const char *str, char *errbuf, int errlen)
384 {
385 /*  Parses string 'str' for the IPMI device password.
386  *    If the option value is the empty string, the IPMI default will be used.
387  *  Returns 0 and updates the 'iopts' struct on success; o/w, returns -1
388  *    (writing an error message into buffer 'errbuf' of length 'errlen').
389  */
390     assert(iopts != NULL);
391     assert(str != NULL);
392 
393     if (str[0] == '\0') {
394         iopts->password[0] = '\0';
395     }
396     else {
397         int n;
398 
399         n = parse_key(iopts->password, str, sizeof(iopts->password));
400 
401         if (n < 0) {
402             if ((errbuf != NULL) && (errlen > 0)) {
403                 snprintf(errbuf, errlen,
404                     "IPMI password exceeds %d-byte maximum",
405                     IPMI_MAX_PSWD_LEN);
406             }
407             return(-1);
408         }
409         if (!ipmiconsole_password_is_valid(iopts->password)) {
410             if ((errbuf != NULL) && (errlen > 0)) {
411                 snprintf(errbuf, errlen, "invalid IPMI password");
412             }
413             return(-1);
414         }
415     }
416     return(0);
417 }
418 
419 
process_ipmi_opt_k_g(ipmiopt_t * iopts,const char * str,char * errbuf,int errlen)420 static int process_ipmi_opt_k_g(
421     ipmiopt_t *iopts, const char *str, char *errbuf, int errlen)
422 {
423 /*  Parses string 'str' for the IPMI device K_g key.
424  *    If the option value is the empty string, the IPMI default will be used.
425  *  Returns 0 and updates the 'iopts' struct on success; o/w, returns -1
426  *    (writing an error message into buffer 'errbuf' of length 'errlen').
427  */
428     assert(iopts != NULL);
429     assert(str != NULL);
430 
431     if (str[0] == '\0') {
432         iopts->kg[0] = '\0';
433         iopts->kgLen = 0;
434     }
435     else {
436         int n;
437 
438         n = parse_key((char *) iopts->kg, str, sizeof(iopts->kg));
439 
440         if (n < 0) {
441             if ((errbuf != NULL) && (errlen > 0)) {
442                 snprintf(errbuf, errlen,
443                     "IPMI K_g exceeds %d-byte maximum",
444                     IPMI_MAX_KG_LEN);
445             }
446             return(-1);
447         }
448         if (!ipmiconsole_k_g_is_valid(iopts->kg, n)) {
449             if ((errbuf != NULL) && (errlen > 0)) {
450                 snprintf(errbuf, errlen, "invalid IPMI K_g");
451             }
452             return(-1);
453         }
454         iopts->kgLen = n;
455     }
456     return(0);
457 }
458 
459 
process_ipmi_opt_privilege(ipmiopt_t * iopts,const char * str,char * errbuf,int errlen)460 static int process_ipmi_opt_privilege(
461     ipmiopt_t *iopts, const char *str, char *errbuf, int errlen)
462 {
463 /*  Parses string 'str' for the IPMI device privilege level.
464  *    If the option value is the empty string, the IPMI default will be used.
465  *  Returns 0 and updates the 'iopts' struct on success; o/w, returns -1
466  *    (writing an error message into buffer 'errbuf' of length 'errlen').
467  */
468     assert(iopts != NULL);
469     assert(str != NULL);
470 
471     if (str[0] == '\0') {
472         iopts->privilegeLevel = -1;
473     }
474     else if (!strcasecmp(str, "user")) {
475         iopts->privilegeLevel = IPMICONSOLE_PRIVILEGE_USER;
476     }
477     else if (!strcasecmp(str, "op") || !strcasecmp(str, "operator")) {
478         iopts->privilegeLevel = IPMICONSOLE_PRIVILEGE_OPERATOR;
479     }
480     else if (!strcasecmp(str, "admin") || !strcasecmp(str, "administrator")) {
481         iopts->privilegeLevel = IPMICONSOLE_PRIVILEGE_ADMIN;
482     }
483     else {
484         long int  n;
485         char     *p;
486 
487         errno = 0;
488         n = strtol(str, &p, 0);
489 
490         if ((*p != '\0') || (errno == ERANGE)) {
491             if ((errbuf != NULL) && (errlen > 0)) {
492                 snprintf(errbuf, errlen,
493                     "invalid IPMI privilege level \"%s\"", str);
494             }
495             return(-1);
496         }
497         if (!ipmiconsole_privilege_level_is_valid(n)) {
498             if ((errbuf != NULL) && (errlen > 0)) {
499                 snprintf(errbuf, errlen,
500                     "invalid IPMI privilege level %ld", n);
501             }
502             return(-1);
503         }
504         iopts->privilegeLevel = n;
505     }
506     return(0);
507 }
508 
509 
process_ipmi_opt_cipher(ipmiopt_t * iopts,const char * str,char * errbuf,int errlen)510 static int process_ipmi_opt_cipher(
511     ipmiopt_t *iopts, const char *str, char *errbuf, int errlen)
512 {
513 /*  Parses string 'str' for the IPMI device cipher suite.
514  *    If the option value is the empty string, the IPMI default will be used.
515  *  Returns 0 and updates the 'iopts' struct on success; o/w, returns -1
516  *    (writing an error message into buffer 'errbuf' of length 'errlen').
517  */
518     assert(iopts != NULL);
519     assert(str != NULL);
520 
521     if (str[0] == '\0') {
522         iopts->cipherSuite = -1;
523     }
524     else {
525         long int  n;
526         char     *p;
527 
528         errno = 0;
529         n = strtol(str, &p, 0);
530 
531         if ((*p != '\0') || (errno == ERANGE)) {
532             if ((errbuf != NULL) && (errlen > 0)) {
533                 snprintf(errbuf, errlen,
534                     "invalid IPMI cipher suite \"%s\"", str);
535             }
536             return(-1);
537         }
538         if (!ipmiconsole_cipher_suite_id_is_valid(n)) {
539             if ((errbuf != NULL) && (errlen > 0)) {
540                 snprintf(errbuf, errlen,
541                     "invalid IPMI cipher suite %ld", n);
542             }
543             return(-1);
544         }
545         iopts->cipherSuite = n;
546     }
547     return(0);
548 }
549 
550 
process_ipmi_opt_workaround(ipmiopt_t * iopts,const char * str,char * errbuf,int errlen)551 static int process_ipmi_opt_workaround(
552     ipmiopt_t *iopts, const char *str, char *errbuf, int errlen)
553 {
554 /*  Parses string 'str' for the IPMI device workaround flag.
555  *    If the option value is the empty string, the IPMI default will be used.
556  *  Returns 0 and updates the 'iopts' struct on success; o/w, returns -1
557  *    (writing an error message into buffer 'errbuf' of length 'errlen').
558  */
559     unsigned int flag;
560 
561     assert(iopts != NULL);
562     assert(str != NULL);
563 
564     if ((str[0] == '\0') || !strcasecmp(str, "default")) {
565         flag = IPMICONSOLE_WORKAROUND_DEFAULT;
566     }
567     else if (!strcasecmp(str, "authcap")) {
568         flag = IPMICONSOLE_WORKAROUND_AUTHENTICATION_CAPABILITIES;
569     }
570     else if (!strcasecmp(str, "intel20")) {
571         flag = IPMICONSOLE_WORKAROUND_INTEL_2_0_SESSION;
572     }
573     else if (!strcasecmp(str, "supermicro20")) {
574         flag = IPMICONSOLE_WORKAROUND_SUPERMICRO_2_0_SESSION;
575     }
576     else if (!strcasecmp(str, "sun20")) {
577         flag = IPMICONSOLE_WORKAROUND_SUN_2_0_SESSION;
578     }
579     else if (!strcasecmp(str, "opensesspriv")) {
580         flag = IPMICONSOLE_WORKAROUND_OPEN_SESSION_PRIVILEGE;
581     }
582     else if (!strcasecmp(str, "integritycheckvalue")) {
583         flag = IPMICONSOLE_WORKAROUND_NON_EMPTY_INTEGRITY_CHECK_VALUE;
584     }
585 #ifdef IPMICONSOLE_WORKAROUND_NO_CHECKSUM_CHECK
586     else if (!strcasecmp(str, "nochecksumcheck")) {
587         flag = IPMICONSOLE_WORKAROUND_NO_CHECKSUM_CHECK;
588     }
589 #endif /* IPMICONSOLE_WORKAROUND_NO_CHECKSUM_CHECK */
590 #ifdef IPMICONSOLE_WORKAROUND_SERIAL_ALERTS_DEFERRED
591     else if (!strcasecmp(str, "serialalertsdeferred")) {
592         flag = IPMICONSOLE_WORKAROUND_SERIAL_ALERTS_DEFERRED;
593     }
594 #endif /* IPMICONSOLE_WORKAROUND_SERIAL_ALERTS_DEFERRED */
595 #ifdef IPMICONSOLE_WORKAROUND_INCREMENT_SOL_PACKET_SEQUENCE
596     else if (!strcasecmp(str, "solpacketseq")) {
597         flag = IPMICONSOLE_WORKAROUND_INCREMENT_SOL_PACKET_SEQUENCE;
598     }
599 #endif /* IPMICONSOLE_WORKAROUND_INCREMENT_SOL_PACKET_SEQUENCE */
600     else if (!strcasecmp(str, "solpayloadsize")) {
601         flag = IPMICONSOLE_WORKAROUND_IGNORE_SOL_PAYLOAD_SIZE;
602     }
603     else if (!strcasecmp(str, "solport")) {
604         flag = IPMICONSOLE_WORKAROUND_IGNORE_SOL_PORT;
605     }
606     else if (!strcasecmp(str, "solstatus")) {
607         flag = IPMICONSOLE_WORKAROUND_SKIP_SOL_ACTIVATION_STATUS;
608     }
609 #ifdef IPMICONSOLE_WORKAROUND_SKIP_CHANNEL_PAYLOAD_SUPPORT
610     else if (!strcasecmp(str, "solchannelsupport")) {
611         flag = IPMICONSOLE_WORKAROUND_SKIP_CHANNEL_PAYLOAD_SUPPORT;
612     }
613 #endif /* IPMICONSOLE_WORKAROUND_SKIP_CHANNEL_PAYLOAD_SUPPORT */
614     else if (!strcasecmp(str, "none")) {
615         flag = 0;
616     }
617     else {
618         unsigned int  u;
619         char         *p;
620 
621         errno = 0;
622         u = strtoul(str, &p, 0);
623 
624         if ((*p != '\0') || (errno == ERANGE)) {
625             if ((errbuf != NULL) && (errlen > 0)) {
626                 snprintf(errbuf, errlen,
627                     "invalid IPMI workaround flag \"%s\"", str);
628             }
629             return(-1);
630         }
631         else if (!ipmiconsole_workaround_flags_is_valid(u)) {
632             if ((errbuf != NULL) && (errlen > 0)) {
633                 snprintf(errbuf, errlen,
634                     "invalid IPMI workaround flag 0x%X", u);
635             }
636             return(-1);
637         }
638         else {
639             flag = u;
640         }
641     }
642 
643     if ((flag == 0) ||
644         (flag == IPMICONSOLE_WORKAROUND_DEFAULT) ||
645         (iopts->workaroundFlags == IPMICONSOLE_WORKAROUND_DEFAULT)) {
646         iopts->workaroundFlags = flag;
647     }
648     else {
649         iopts->workaroundFlags |= flag;
650     }
651     return(0);
652 }
653 
654 
parse_key(char * dst,const char * src,size_t dstlen)655 static int parse_key(char *dst, const char *src, size_t dstlen)
656 {
657 /*  Parses the NUL-terminated key string 'src', writing the result into buffer
658  *    'dst' of length 'dstlen'.  The 'dst' buffer will be NUL-terminated if
659  *    'dstlen' > 0.
660  *  The 'src' is interpreted as ASCII text unless it is prefixed with
661  *    "0x" or "0X" and contains only hexadecimal digits (ie, [0-9A-Fa-f]).
662  *    A hexadecimal string will be converted to binary and may contain
663  *    embedded NUL characters.
664  *  Returns the length of the key (in bytes) written to 'dst'
665  *    (not including the terminating null character),
666  *    or -1 if truncation occurred.
667  */
668     const char *hexdigits = "0123456789ABCDEFabcdef";
669     char       *dstend;
670     char       *p;
671     char       *q;
672     int         n;
673 
674     assert(dst != NULL);
675     assert(src != NULL);
676 
677     if (dstlen == 0) {
678         return(-1);
679     }
680     if ((src[0] == '0') && (src[1] == 'x' || src[1] == 'X')
681             && (strspn(src + 2, hexdigits) == strlen(src + 2))) {
682         dstend = dst + dstlen - 1;      /* reserve space for terminating NUL */
683         p = (char *) src + 2;
684         q = dst;
685         n = 0;
686         while (*p && (q < dstend)) {
687             if (((p - src) & 0x01) == 0) {
688                 *q = (toint(*p++) << 4) & 0xf0;
689                 n++;
690             }
691             else {
692                 *q++ |= (toint(*p++)) & 0x0f;
693             }
694         }
695         dst[n] = '\0';
696         if (*p) {
697             return(-1);
698         }
699     }
700     else {
701         n = strlcpy(dst, src, dstlen);
702         if ((size_t) n >= dstlen) {
703             return(-1);
704         }
705     }
706     assert((n >= 0) && ((size_t) n < dstlen));
707     return(n);
708 }
709 
710 
create_ipmi_obj(server_conf_t * conf,char * name,ipmiopt_t * iconf,char * host,char * errbuf,int errlen)711 obj_t * create_ipmi_obj(server_conf_t *conf, char *name,
712     ipmiopt_t *iconf, char *host, char *errbuf, int errlen)
713 {
714 /*  Creates a new IPMI device object and adds it to the master objs list.
715  *  Returns the new object, or NULL on error.
716  */
717     ListIterator i;
718     obj_t *ipmi;
719 
720     assert(conf != NULL);
721     assert((name != NULL) && (name[0] != '\0'));
722     assert(iconf != NULL);
723 
724     /*  Check for duplicate console names.
725      */
726     i = list_iterator_create(conf->objs);
727     while ((ipmi = list_next(i))) {
728         if (is_console_obj(ipmi) && !strcmp(ipmi->name, name)) {
729             if ((errbuf != NULL) && (errlen > 0)) {
730                 snprintf(errbuf, errlen,
731                     "console [%s] specifies duplicate console name", name);
732             }
733             break;
734         }
735         if (is_ipmi_obj(ipmi) && !strcmp(ipmi->aux.ipmi.host, host)) {
736             if ((errbuf != NULL) && (errlen > 0)) {
737                 snprintf(errbuf, errlen,
738                     "console [%s] specifies duplicate hostname \"%s\"",
739                     name, host);
740             }
741             break;
742         }
743     }
744     list_iterator_destroy(i);
745     if (ipmi != NULL) {
746         return(NULL);
747     }
748     ipmi = create_obj(conf, name, -1, CONMAN_OBJ_IPMI);
749     ipmi->aux.ipmi.host = create_string(host);
750     ipmi->aux.ipmi.iconf = *iconf;
751     ipmi->aux.ipmi.ctx = NULL;
752     ipmi->aux.ipmi.logfile = NULL;
753     ipmi->aux.ipmi.state = CONMAN_IPMI_DOWN;
754     ipmi->aux.ipmi.timer = -1;
755     ipmi->aux.ipmi.delay = IPMI_MIN_TIMEOUT;
756     x_pthread_mutex_init(&ipmi->aux.ipmi.mutex, NULL);
757     conf->numIpmiObjs++;
758     /*
759      *  Add obj to the master conf->objs list.
760      */
761     list_append(conf->objs, ipmi);
762 
763     DPRINTF((10,
764         " IPMI [%s] H:%s U:%s P:%s K:%s L:%d C:%d W:0x%X\n",
765         ipmi->name, ipmi->aux.ipmi.host, ipmi->aux.ipmi.iconf.username,
766         ipmi->aux.ipmi.iconf.password, ipmi->aux.ipmi.iconf.kg,
767         ipmi->aux.ipmi.iconf.privilegeLevel, ipmi->aux.ipmi.iconf.cipherSuite,
768         ipmi->aux.ipmi.iconf.workaroundFlags));
769     return(ipmi);
770 }
771 
772 
open_ipmi_obj(obj_t * ipmi)773 int open_ipmi_obj(obj_t *ipmi)
774 {
775 /*  (Re)opens the specified 'ipmi' obj.
776  *    A no-op is performed if the connection is already in the pending state.
777  *  Returns 0 if the IPMI console is successfully opened; o/w, returns -1.
778  */
779     int rc = 0;
780     ipmi_state_t state;
781 
782     assert(ipmi != NULL);
783     assert(is_ipmi_obj(ipmi));
784 
785     x_pthread_mutex_lock(&ipmi->aux.ipmi.mutex);
786     state = ipmi->aux.ipmi.state;
787     x_pthread_mutex_unlock(&ipmi->aux.ipmi.mutex);
788 
789     if (state == CONMAN_IPMI_UP) {
790         disconnect_ipmi_obj(ipmi);
791         rc = connect_ipmi_obj(ipmi);
792     }
793     else if (state == CONMAN_IPMI_DOWN) {
794         rc = connect_ipmi_obj(ipmi);
795     }
796     DPRINTF((9, "Opened [%s] via IPMI: fd=%d host=%s state=%d.\n",
797         ipmi->name, ipmi->fd, ipmi->aux.ipmi.host,
798         (int) ipmi->aux.ipmi.state));
799     return(rc);
800 }
801 
802 
disconnect_ipmi_obj(obj_t * ipmi)803 static void disconnect_ipmi_obj(obj_t *ipmi)
804 {
805 /*  Closes the existing connection with the specified 'ipmi' obj.
806  */
807     DPRINTF((10, "Disconnecting from <%s> via IPMI for [%s].\n",
808         ipmi->aux.ipmi.host, ipmi->name));
809 
810     x_pthread_mutex_lock(&ipmi->aux.ipmi.mutex);
811 
812     if (ipmi->aux.ipmi.timer >= 0) {
813         (void) tpoll_timeout_cancel(tp_global, ipmi->aux.ipmi.timer);
814         ipmi->aux.ipmi.timer = -1;
815     }
816     if (ipmi->fd >= 0) {
817         tpoll_clear(tp_global, ipmi->fd, POLLIN | POLLOUT);
818         if (close(ipmi->fd) < 0) {
819             log_msg(LOG_WARNING,
820                 "Unable to close connection to <%s> for console [%s]: %s",
821                 ipmi->aux.ipmi.host, ipmi->name, strerror(errno));
822         }
823         ipmi->fd = -1;
824     }
825     /*  Notify linked objs when transitioning from an UP state.
826      */
827     if (ipmi->aux.ipmi.state == CONMAN_IPMI_UP) {
828         write_notify_msg(ipmi, LOG_INFO,
829             "Console [%s] disconnected from <%s>",
830             ipmi->name, ipmi->aux.ipmi.host);
831     }
832     ipmi->aux.ipmi.state = CONMAN_IPMI_DOWN;
833 
834     x_pthread_mutex_unlock(&ipmi->aux.ipmi.mutex);
835 
836     return;
837 }
838 
839 
connect_ipmi_obj(obj_t * ipmi)840 static int connect_ipmi_obj(obj_t *ipmi)
841 {
842 /*  Establishes a non-blocking connect with the specified (ipmi) obj.
843  *  Returns 0 if the connection is successfully completed; o/w, returns -1.
844  *
845  *  Note that this routine can be invoked from both the main thread and the
846  *    ipmiconsole engine thread.
847  */
848     int rc = 0;
849 
850     x_pthread_mutex_lock(&ipmi->aux.ipmi.mutex);
851 
852     /*  The if-guard for a !UP state is to protect against a race-condition
853      *    where both the main thread and the ipmiconsole engine thread call
854      *    this routine at the same time.  If the first thread successfully
855      *    completes the connection and transitions to an UP state, it will
856      *    set a reset_ipmi_delay timer.  The if-guard protects against the
857      *    subsequent thread cancelling this timer.
858      *  If the first thread fails to complete the connection, the subsequent
859      *    thread will just retry a bit sooner than planned.
860      */
861     if (ipmi->aux.ipmi.state != CONMAN_IPMI_UP) {
862 
863         if (ipmi->aux.ipmi.timer >= 0) {
864             (void) tpoll_timeout_cancel(tp_global, ipmi->aux.ipmi.timer);
865             ipmi->aux.ipmi.timer = -1;
866         }
867         if (ipmi->aux.ipmi.state == CONMAN_IPMI_DOWN) {
868             rc = initiate_ipmi_connect(ipmi);
869         }
870         else if (ipmi->aux.ipmi.state == CONMAN_IPMI_PENDING) {
871             rc = complete_ipmi_connect(ipmi);
872         }
873         else {
874             log_err(0, "Console [%s] in unexpected IPMI state=%d",
875                 ipmi->name, (int) ipmi->aux.ipmi.state);
876         }
877         if (rc < 0) {
878             fail_ipmi_connect(ipmi);
879         }
880     }
881     x_pthread_mutex_unlock(&ipmi->aux.ipmi.mutex);
882     return(rc);
883 }
884 
885 
initiate_ipmi_connect(obj_t * ipmi)886 static int initiate_ipmi_connect(obj_t *ipmi)
887 {
888 /*  Initiates an IPMI connection attempt.
889  *  Returns 0 if the connection initiation is successful, or -1 on error.
890  *
891  *  XXX: This routine assumes the ipmi obj mutex is already locked.
892  */
893     int rc;
894 
895     assert(ipmi->aux.ipmi.state == CONMAN_IPMI_DOWN);
896 
897     if (create_ipmi_ctx(ipmi) < 0) {
898         return(-1);
899     }
900     DPRINTF((10, "Connecting to <%s> via IPMI for [%s].\n",
901         ipmi->aux.ipmi.host, ipmi->name));
902 
903     rc = ipmiconsole_engine_submit(ipmi->aux.ipmi.ctx,
904         (Ipmiconsole_callback) connect_ipmi_obj, ipmi);
905     if (rc < 0) {
906         return(-1);
907     }
908     ipmi->aux.ipmi.state = CONMAN_IPMI_PENDING;
909     /*
910      *  ipmiconsole_engine_submit() should always call its callback function,
911      *    at which point the connection will be established or retried.
912      *  This timer is simply an additional safety measure in case it doesn't.
913      *  Any existing timer should have already been cancelled at the start of
914      *    connect_ipmi_obj().
915      */
916     assert(ipmi->aux.ipmi.timer == -1);
917     ipmi->aux.ipmi.timer = tpoll_timeout_relative(tp_global,
918         (callback_f) connect_ipmi_obj, ipmi,
919         IPMI_CONNECT_TIMEOUT * 1000);
920 
921     return(0);
922 }
923 
924 
create_ipmi_ctx(obj_t * ipmi)925 static int create_ipmi_ctx(obj_t *ipmi)
926 {
927 /*  Creates a new IPMI context 'ipmi'.
928  *  Returns 0 if the context is successfully created; o/w, returns -1.
929  *
930  *  XXX: This routine assumes the ipmi obj mutex is already locked.
931  */
932     struct ipmiconsole_ipmi_config ipmi_config;
933     struct ipmiconsole_protocol_config protocol_config;
934     struct ipmiconsole_engine_config engine_config;
935 
936     ipmi_config.username = ipmi->aux.ipmi.iconf.username;
937     ipmi_config.password = ipmi->aux.ipmi.iconf.password;
938     ipmi_config.k_g = ipmi->aux.ipmi.iconf.kg;
939     ipmi_config.k_g_len = ipmi->aux.ipmi.iconf.kgLen;
940     ipmi_config.privilege_level = ipmi->aux.ipmi.iconf.privilegeLevel;
941     ipmi_config.cipher_suite_id = ipmi->aux.ipmi.iconf.cipherSuite;
942     ipmi_config.workaround_flags = ipmi->aux.ipmi.iconf.workaroundFlags;
943 
944     protocol_config.session_timeout_len = -1;
945     protocol_config.retransmission_timeout_len = -1;
946     protocol_config.retransmission_backoff_count = -1;
947     protocol_config.keepalive_timeout_len = -1;
948     protocol_config.retransmission_keepalive_timeout_len = -1;
949     protocol_config.acceptable_packet_errors_count = -1;
950     protocol_config.maximum_retransmission_count = -1;
951 
952     engine_config.engine_flags = IPMICONSOLE_ENGINE_DEFAULT;
953     engine_config.behavior_flags = IPMICONSOLE_BEHAVIOR_DEFAULT;
954     engine_config.debug_flags = IPMICONSOLE_DEBUG_DEFAULT;
955 
956     /*  A context cannot be submitted to the ipmiconsole engine more than once,
957      *    so create a new context if one already exists.
958      */
959     if (ipmi->aux.ipmi.ctx) {
960         ipmiconsole_ctx_destroy(ipmi->aux.ipmi.ctx);
961     }
962     ipmi->aux.ipmi.ctx = ipmiconsole_ctx_create(
963         ipmi->aux.ipmi.host, &ipmi_config, &protocol_config, &engine_config);
964 
965     if (!ipmi->aux.ipmi.ctx) {
966         return(-1);
967     }
968     return(0);
969 }
970 
971 
complete_ipmi_connect(obj_t * ipmi)972 static int complete_ipmi_connect(obj_t *ipmi)
973 {
974 /*  Completes an IPMI connection attempt.
975  *  Returns 0 if the connection is successfully established, or -1 on error.
976  *
977  *  XXX: This routine assumes the ipmi obj mutex is already locked.
978  */
979     ipmiconsole_ctx_status_t status;
980 
981     assert(ipmi->aux.ipmi.state == CONMAN_IPMI_PENDING);
982 
983     status = ipmiconsole_ctx_status(ipmi->aux.ipmi.ctx);
984     if (status != IPMICONSOLE_CTX_STATUS_SOL_ESTABLISHED) {
985         return(-1);
986     }
987     if ((ipmi->fd = ipmiconsole_ctx_fd(ipmi->aux.ipmi.ctx)) < 0) {
988         return(-1);
989     }
990     set_fd_nonblocking(ipmi->fd);
991     set_fd_closed_on_exec(ipmi->fd);
992 
993     ipmi->gotEOF = 0;
994     ipmi->aux.ipmi.state = CONMAN_IPMI_UP;
995     tpoll_set(tp_global, ipmi->fd, POLLIN);
996 
997     /*  Require the connection to be up for a minimum length of time
998      *    before resetting the reconnect delay back to the minimum.
999      *  Any existing timer should have already been cancelled at the start of
1000      *    connect_ipmi_obj().
1001      */
1002     assert(ipmi->aux.ipmi.timer == -1);
1003     ipmi->aux.ipmi.timer = tpoll_timeout_relative(tp_global,
1004         (callback_f) reset_ipmi_delay, ipmi, IPMI_MIN_TIMEOUT * 1000);
1005 
1006     /*  Notify linked objs when transitioning into an UP state.
1007      */
1008     write_notify_msg(ipmi, LOG_INFO, "Console [%s] connected to <%s>",
1009         ipmi->name, ipmi->aux.ipmi.host);
1010     DPRINTF((15, "Connection established to <%s> via IPMI for [%s].\n",
1011         ipmi->aux.ipmi.host, ipmi->name));
1012     return (0);
1013 }
1014 
1015 
fail_ipmi_connect(obj_t * ipmi)1016 static void fail_ipmi_connect(obj_t *ipmi)
1017 {
1018 /*  Logs an error message for the connection failure and sets a timer to
1019  *    establish a new connection attempt.
1020  *
1021  *  XXX: This routine assumes the ipmi obj mutex is already locked.
1022  */
1023     ipmi->aux.ipmi.state = CONMAN_IPMI_DOWN;
1024 
1025     if (!ipmi->aux.ipmi.ctx) {
1026         log_msg(LOG_INFO,
1027             "Unable to create IPMI context for [%s]", ipmi->name);
1028     }
1029     else {
1030         int e = ipmiconsole_ctx_errnum(ipmi->aux.ipmi.ctx);
1031         log_msg(LOG_INFO,
1032             "Unable to connect to <%s> via IPMI for [%s]%s%s",
1033             ipmi->aux.ipmi.host, ipmi->name, (e ? ": " : ""),
1034             (e ? ipmiconsole_ctx_strerror(e) : ""));
1035     }
1036     /*  Set timer for establishing new connection attempt.
1037      *  Any existing timer should have already been cancelled at the start of
1038      *    connect_ipmi_obj().
1039      */
1040     assert(ipmi->aux.ipmi.delay >= IPMI_MIN_TIMEOUT);
1041     assert(ipmi->aux.ipmi.delay <= IPMI_MAX_TIMEOUT);
1042     DPRINTF((15, "Reconnect attempt to <%s> via IPMI for [%s] in %ds.\n",
1043         ipmi->aux.ipmi.host, ipmi->name, ipmi->aux.ipmi.delay));
1044     assert(ipmi->aux.ipmi.timer == -1);
1045     ipmi->aux.ipmi.timer = tpoll_timeout_relative(tp_global,
1046         (callback_f) connect_ipmi_obj, ipmi,
1047         ipmi->aux.ipmi.delay * 1000);
1048 
1049     /*  Update timer delay via exponential backoff.
1050      */
1051     if (ipmi->aux.ipmi.delay < IPMI_MAX_TIMEOUT) {
1052         ipmi->aux.ipmi.delay = MIN(ipmi->aux.ipmi.delay * 2, IPMI_MAX_TIMEOUT);
1053     }
1054     return;
1055 }
1056 
1057 
reset_ipmi_delay(obj_t * ipmi)1058 static void reset_ipmi_delay(obj_t *ipmi)
1059 {
1060 /*  Resets the 'ipmi' obj's delay between reconnect attempts.
1061  */
1062     assert(is_ipmi_obj(ipmi));
1063 
1064     x_pthread_mutex_lock(&ipmi->aux.ipmi.mutex);
1065 
1066     ipmi->aux.ipmi.delay = IPMI_MIN_TIMEOUT;
1067 
1068     /*  Also reset the timer ID since this routine is only invoked via a timer.
1069      */
1070     ipmi->aux.ipmi.timer = -1;
1071 
1072     x_pthread_mutex_unlock(&ipmi->aux.ipmi.mutex);
1073     return;
1074 }
1075 
1076 
send_ipmi_break(obj_t * ipmi)1077 int send_ipmi_break(obj_t *ipmi)
1078 {
1079 /*  Generates a serial-break for the specified 'ipmi' obj.
1080  *  Returns 0 on success; o/w, returns -1.
1081  */
1082     int rc;
1083 
1084     assert(ipmi != NULL);
1085     assert(is_ipmi_obj(ipmi));
1086 
1087     x_pthread_mutex_lock(&ipmi->aux.ipmi.mutex);
1088     if (!ipmi->aux.ipmi.ctx) {
1089         log_msg(LOG_ERR,
1090             "Unable to send serial-break to [%s]: NULL IPMI context",
1091             ipmi->name);
1092         rc = -1;
1093     }
1094     else {
1095         rc = ipmiconsole_ctx_generate_break(ipmi->aux.ipmi.ctx);
1096     }
1097     x_pthread_mutex_unlock(&ipmi->aux.ipmi.mutex);
1098     return(rc);
1099 }
1100