1 /* string.c - IPMI string handling
2  * Copyright (C) 2012 MontaVista Software.
3  * Corey Minyard <cminyard@mvista.com>
4  *
5  * This file is part of the IPMI Interface (IPMIIF).
6  *
7  * This is for handling strings in SDRs and FRU data.
8  *
9  * This software is available to you under a choice of one of two
10  * licenses.  You may choose to be licensed under the terms of the GNU
11  * Lesser General Public License (GPL) Version 2 or the modified BSD
12  * license below.  The following disclamer applies to both licenses:
13  *
14  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
15  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
20  *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21  *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
22  *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
23  *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  * GNU Lesser General Public Licence
26  *
27  *  This program is free software; you can redistribute it and/or
28  *  modify it under the terms of the GNU Lesser General Public License
29  *  as published by the Free Software Foundation; either version 2 of
30  *  the License, or (at your option) any later version.
31  *
32  *  You should have received a copy of the GNU Lesser General Public
33  *  License along with this program; if not, write to the Free
34  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35  *
36  * Modified BSD Licence
37  *
38  * Redistribution and use in source and binary forms, with or without
39  * modification, are permitted provided that the following conditions
40  * are met:
41  *
42  *   1. Redistributions of source code must retain the above copyright
43  *      notice, this list of conditions and the following disclaimer.
44  *   2. Redistributions in binary form must reproduce the above
45  *      copyright notice, this list of conditions and the following
46  *      disclaimer in the documentation and/or other materials provided
47  *      with the distribution.
48  *   3. The name of the author may not be used to endorse or promote
49  *      products derived from this software without specific prior
50  *      written permission.
51  */
52 
53 #include <string.h>
54 #include <errno.h>
55 
56 #include <OpenIPMI/ipmi_string.h>
57 
58 static int
ipmi_get_unicode(unsigned int len,unsigned char ** d,unsigned int in_len,char * out,unsigned int out_len)59 ipmi_get_unicode(unsigned int len,
60 		 unsigned char **d, unsigned int in_len,
61 		 char *out, unsigned int out_len)
62 {
63     if (in_len < len)
64 	return -1;
65     if (out_len < len)
66 	return -1;
67 
68     memcpy(out, *d, len);
69     *d += len;
70     return len;
71 }
72 
73 static int
ipmi_get_bcd_plus(unsigned int len,unsigned char ** d,unsigned int in_len,char * out,unsigned int out_len)74 ipmi_get_bcd_plus(unsigned int len,
75 		  unsigned char **d, unsigned int in_len,
76 		  char *out, unsigned int out_len)
77 {
78     static char table[16] = {
79 	'0', '1', '2', '3', '4', '5', '6', '7',
80 	'8', '9', ' ', '-', '.', ':', ',', '_'
81     };
82     unsigned int bo;
83     unsigned int val = 0;
84     unsigned int i;
85     unsigned int real_length;
86     char         *out_s = out;
87 
88     real_length = (in_len * 8) / 4;
89     if (len > real_length)
90 	return -1;
91     if (len > out_len)
92 	return -1;
93 
94     bo = 0;
95     for (i=0; i<len; i++) {
96 	switch (bo) {
97 	case 0:
98 	    val = **d & 0xf;
99 	    bo = 4;
100 	    break;
101 	case 4:
102 	    val = (**d >> 4) & 0xf;
103 	    (*d)++;
104 	    bo = 0;
105 	    break;
106 	}
107 	*out = table[val];
108 	out++;
109     }
110 
111     if (bo != 0)
112 	(*d)++;
113 
114     return out - out_s;
115 }
116 
117 static int
ipmi_get_6_bit_ascii(unsigned int len,unsigned char ** d,unsigned int in_len,char * out,unsigned int out_len)118 ipmi_get_6_bit_ascii(unsigned int len,
119 		     unsigned char **d, unsigned int in_len,
120 		     char *out, unsigned int out_len)
121 {
122     static char table[64] = {
123 	' ', '!', '"', '#', '$', '%', '&', '\'',
124 	'(', ')', '*', '+', ',', '-', '.', '/',
125 	'0', '1', '2', '3', '4', '5', '6', '7',
126 	'8', '9', ':', ';', '<', '=', '>', '?',
127 	'&', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
128 	'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
129 	'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
130 	'X', 'Y', 'Z', '[', '\\', ']', '^', '_'
131     };
132     unsigned int bo;
133     unsigned int val = 0;
134     unsigned int i;
135     unsigned int real_length;
136     char         *out_s = out;
137 
138     real_length = (in_len * 8) / 6;
139     if (len > real_length)
140 	return -1;
141     if (len > out_len)
142 	return -1;
143 
144     bo = 0;
145     for (i=0; i<len; i++) {
146 	switch (bo) {
147 	case 0:
148 	    val = **d & 0x3f;
149 	    bo = 6;
150 	    break;
151 	case 2:
152 	    val = (**d >> 2) & 0x3f;
153 	    (*d)++;
154 	    bo = 0;
155 	    break;
156 	case 4:
157 	    val = (**d >> 4) & 0xf;
158 	    (*d)++;
159 	    val |= (**d & 0x3) << 4;
160 	    bo = 2;
161 	    break;
162 	case 6:
163 	    val = (**d >> 6) & 0x3;
164 	    (*d)++;
165 	    val |= (**d & 0xf) << 2;
166 	    bo = 4;
167 	    break;
168 	}
169 	*out = table[val];
170 	out++;
171     }
172 
173     if (bo != 0)
174 	(*d)++;
175 
176     return out - out_s;
177 }
178 
179 static int
ipmi_get_8_bit_ascii(unsigned int len,unsigned char ** d,unsigned int in_len,char * out,unsigned int out_len)180 ipmi_get_8_bit_ascii(unsigned int len,
181 		     unsigned char **d, unsigned int in_len,
182 		     char *out, unsigned int out_len)
183 {
184     unsigned int j;
185 
186     if (len > in_len)
187 	return -1;
188     if (len > out_len)
189 	return -1;
190 
191     for (j=0; j<len; j++) {
192 	*out = **d;
193 	out++;
194 	(*d)++;
195     }
196     return len;
197 };
198 
199 int
ipmi_get_device_string(unsigned char ** const pinput,unsigned int in_len,char * output,int semantics,int force_unicode,enum ipmi_str_type_e * stype,unsigned int max_out_len,unsigned int * out_len)200 ipmi_get_device_string(unsigned char        ** const pinput,
201 		       unsigned int         in_len,
202 		       char                 *output,
203 		       int                  semantics,
204 		       int                  force_unicode,
205 		       enum ipmi_str_type_e *stype,
206 		       unsigned int         max_out_len,
207 		       unsigned int         *out_len)
208 {
209     int type;
210     int len;
211     int olen;
212 
213     if (max_out_len == 0)
214 	return 0;
215 
216     if (in_len <= 0) {
217 	*output = '\0';
218 	return 0;
219     }
220 
221 #if 0
222     /* Note that this is technically correct, but commonly ignored.
223        0xc1 is invalid, but some FRU and SDR data still uses it.  Grr.
224        The FRU stuff has to handle the end-of-area marker c1 itself,
225        anyway, so this is relatively safe.  In a "correct" system you
226        should never see a 0xc1 here, anyway. */
227     if (**pinput == 0xc1) {
228 	*output = '\0';
229 	(*pinput)++;
230 	return 0;
231     }
232 #endif
233 
234     type = (**pinput >> 6) & 3;
235 
236     /* Special case for FRU data, type 3 is unicode if the language is
237        non-english. */
238     if ((force_unicode) && (type == 3)) {
239 	type = 0;
240 	force_unicode = 0;
241     }
242 
243     len = **pinput & 0x3f;
244     (*pinput)++;
245     in_len--;
246     *stype = IPMI_ASCII_STR;
247     switch (type)
248     {
249 	case 0: /* Unicode */
250 	    olen = ipmi_get_unicode(len, pinput, in_len, output, max_out_len);
251 	    if (semantics == IPMI_STR_FRU_SEMANTICS)
252 		*stype = IPMI_BINARY_STR;
253 	    else
254 		*stype = IPMI_UNICODE_STR;
255 	    break;
256 	case 1: /* BCD Plus */
257 	    olen = ipmi_get_bcd_plus(len, pinput, in_len, output, max_out_len);
258 	    break;
259 	case 2: /* 6-bit ASCII */
260 	    olen=ipmi_get_6_bit_ascii(len, pinput, in_len, output, max_out_len);
261 	    break;
262 	case 3: /* 8-bit ASCII */
263 	    olen=ipmi_get_8_bit_ascii(len, pinput, in_len, output, max_out_len);
264 	    break;
265         default:
266 	    olen = 0;
267     }
268 
269     if (olen < 0)
270 	return EINVAL;
271 
272     *out_len = olen;
273     return 0;
274 }
275 
276 /* Element will be zero if not present, n-1 if present. */
277 static char table_4_bit[256] =
278 {
279     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
280     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
281     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
282     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
283     0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
284     0x00, 0x00, 0x00, 0x00, 0x0f, 0x0c, 0x0d, 0x00,
285     0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
286     0x09, 0x0a, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
287     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
288     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
289     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
290     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
291     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
292     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
293     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
294     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
295     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
296     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
297     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
298     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
299     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
300     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
301     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
302     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
303     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
304     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
305     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
306     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
307     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
308     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
309     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
310     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
311 };
312 
313 /* Element will be zero if not present, n-1 if present. */
314 static char table_6_bit[256] =
315 {
316     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
317     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
318     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
319     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
320     0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x21, 0x08,
321     0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
322     0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
323     0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
324     0x00, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
325     0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
326     0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
327     0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40,
328     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
329     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
330     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
331     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
332     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
333     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
334     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
335     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
336     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
337     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
338     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
339     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
340     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
341     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
342     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
343     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
344     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
345     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
346     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
347     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
348 };
349 
350 static void
ipmi_set_bcdplus(const char * input,unsigned int in_len,unsigned char * output,unsigned int * out_len)351 ipmi_set_bcdplus(const char    *input,
352 		 unsigned int  in_len,
353 		 unsigned char *output,
354 		 unsigned int  *out_len)
355 {
356     unsigned int len = *out_len;
357     const char   *s = input;
358     unsigned int pos = 0;
359     unsigned int bit = 0;
360     unsigned int count = 0;
361 
362     while (in_len > 0) {
363 	switch(bit) {
364 	    case 0:
365 		pos++;
366 		if (pos >= len)
367 		    goto out_overflow;
368 		output[pos] = table_4_bit[(int) *s] - 1;
369 		bit = 4;
370 		break;
371 
372 	    case 4:
373 		output[pos] |= (table_4_bit[(int) *s] - 1) << 4;
374 		bit = 0;
375 		break;
376 	}
377 	count++;
378 	in_len--;
379 	s++;
380     }
381     pos++;
382  out_overflow:
383     output[0] = (0x01 << 6) | count;
384     *out_len = pos;
385 }
386 
387 static void
ipmi_set_6_bit_ascii(const char * input,unsigned int in_len,unsigned char * output,unsigned int * out_len)388 ipmi_set_6_bit_ascii(const char    *input,
389 		     unsigned int  in_len,
390 		     unsigned char *output,
391 		     unsigned int  *out_len)
392 {
393     unsigned int len = *out_len;
394     const char   *s = input;
395     unsigned int pos = 0;
396     unsigned int bit = 0;
397     unsigned int count = 0;
398     unsigned int cval;
399     unsigned int oval;
400 
401     while (in_len > 0) {
402 	cval = *s;
403 	s++;
404 	oval = table_6_bit[cval] - 1;
405 	switch(bit) {
406 	case 0:
407 	    pos++;
408 	    if (pos >= len)
409 		goto out_overflow;
410 	    output[pos] = oval;
411 	    bit = 6;
412 	    break;
413 
414 	case 2:
415 	    output[pos] |= oval << 2;
416 	    bit = 0;
417 	    break;
418 
419 	case 4:
420 	    output[pos] |= oval << 4;
421 	    pos++;
422 	    if (pos >= len)
423 		goto out_overflow;
424 	    output[pos] = (oval >> 4) & 0x3;
425 	    bit = 2;
426 	    break;
427 
428 	case 6:
429 	    output[pos] |= oval << 6;
430 	    pos++;
431 	    if (pos >= len)
432 		goto out_overflow;
433 	    output[pos] = (oval >> 2) & 0xf;
434 	    bit = 4;
435 	    break;
436 	}
437 	count++;
438 	in_len--;
439     }
440     pos++;
441  out_overflow:
442     output[0] = (0x02 << 6) | count;
443     *out_len = pos;
444 }
445 
446 static void
ipmi_set_8_bit_ascii(const char * input,unsigned int in_len,unsigned char * output,unsigned int * out_len)447 ipmi_set_8_bit_ascii(const char    *input,
448 		     unsigned int  in_len,
449 		     unsigned char *output,
450 		     unsigned int  *out_len)
451 {
452     char tmp[2];
453     /* truncate if necessary. */
454     if (in_len > (*out_len - 1))
455 	in_len = *out_len - 1;
456 
457     /* A length of 1 is invalid, make it 2 with a nil char */
458     if (in_len == 1) {
459 	tmp[0] = input[0];
460 	tmp[1] = '\0';
461 	input = tmp;
462 	in_len++;
463     }
464 
465     *out_len = in_len + 1;
466 
467     memcpy(output+1, input, in_len);
468     output[0] = (0x03 << 6) | in_len;
469 }
470 
471 void
ipmi_set_device_string2(const char * input,enum ipmi_str_type_e type,unsigned int in_len,unsigned char * output,int force_unicode,unsigned int * out_len,unsigned int options)472 ipmi_set_device_string2(const char           *input,
473 			enum ipmi_str_type_e type,
474 			unsigned int         in_len,
475 			unsigned char        *output,
476 			int                  force_unicode,
477 			unsigned int         *out_len,
478 			unsigned int         options)
479 {
480     const char   *s = input;
481     int          bsize = 0; /* Start with 4-bit. */
482     unsigned int i;
483 
484     /* Max size is 64 (63 bytes + the type byte). */
485     if (*out_len > 64)
486 	*out_len = 64;
487     /* Truncate */
488     if (in_len > 63)
489 	in_len = 63;
490 
491     if (type == IPMI_ASCII_STR) {
492 	if (options && IPMI_STRING_OPTION_8BIT_ONLY)
493 	    bsize = 2;
494 	else {
495 	    for (i=0; i<in_len; i++) {
496 		if (table_4_bit[(int) *s] == 0) {
497 		    bsize |= 1;
498 		    if (table_6_bit[(int) *s] == 0) {
499 			bsize |= 2;
500 			break;
501 		    }
502 		}
503 		s++;
504 	    }
505 	}
506 	if (bsize == 0) {
507 	    /* We can encode it in 4-bit BCD+ */
508 	    ipmi_set_bcdplus(input, in_len, output, out_len);
509 	} else if (bsize == 1) {
510 	    /* We can encode it in 6-bit ASCII. */
511 	    ipmi_set_6_bit_ascii(input, in_len, output, out_len);
512 	} else {
513 	    ipmi_set_8_bit_ascii(input, in_len, output, out_len);
514 	}
515     } else {
516 	/* The input and output are unicode. */
517 	if (in_len > *out_len-1)
518 	    in_len = *out_len-1;
519 	if ((force_unicode) && (type == IPMI_UNICODE_STR))
520 	    *output = (0x3 << 6) | in_len;
521 	else
522 	    *output = in_len;
523 	memcpy(output+1, input, in_len);
524 	*out_len = in_len + 1;
525     }
526 }
527 
528 void
ipmi_set_device_string(const char * input,enum ipmi_str_type_e type,unsigned int in_len,unsigned char * output,int force_unicode,unsigned int * out_len)529 ipmi_set_device_string(const char           *input,
530 		       enum ipmi_str_type_e type,
531 		       unsigned int         in_len,
532 		       unsigned char        *output,
533 		       int                  force_unicode,
534 		       unsigned int         *out_len)
535 {
536     ipmi_set_device_string2(input, type, in_len, output, force_unicode,
537 			    out_len, IPMI_STRING_OPTION_NONE);
538 }
539