libblkid: care about unsafe chars in cache

The high-level libblkid API uses /run/blkid/blkid.tab cache to
store probing results. The cache format is

   <device NAME="value" ...>devname</device>

and unfortunately the cache code does not escape quotation marks:

   # mkfs.ext4 -L 'AAA"BBB'

   # cat /run/blkid/blkid.tab
   ...
   <device ... LABEL="AAA"BBB" ...>/dev/sdb1</device>

such string is later incorrectly parsed and blkid(8) returns
nonsenses. And for use-cases like

   # eval $(blkid -o export /dev/sdb1)

it's also insecure.

Note that mount, udevd and blkid -p are based on low-level libblkid
API, it bypass the cache and directly read data from the devices.

The current udevd upstream does not depend on blkid(8) output at all,
it's directly linked with the library and all unsafe chars are encoded by
\x<hex> notation.

   # mkfs.ext4 -L 'X"`/tmp/foo` "' /dev/sdb1
   # udevadm info --export-db | grep LABEL
   ...
   E: ID_FS_LABEL=X__/tmp/foo___
   E: ID_FS_LABEL_ENC=X\x22\x60\x2ftmp\x2ffoo\x60\x20\x22

Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
Karel Zak 2014-11-27 13:39:35 +01:00
parent cca51b9e45
commit 89e90ae7b2
4 changed files with 45 additions and 7 deletions

View File

@ -252,15 +252,30 @@ static int parse_token(char **name, char **value, char **cp)
*value = skip_over_blank(*value + 1);
if (**value == '"') {
end = strchr(*value + 1, '"');
if (!end) {
char *p = end = *value + 1;
/* convert 'foo\"bar' to 'foo"bar' */
while (*p) {
if (*p == '\\') {
p++;
*end = *p;
} else {
*end = *p;
if (*p == '"')
break;
}
p++;
end++;
}
if (*end != '"') {
DBG(READ, ul_debug("unbalanced quotes at: %s", *value));
*cp = *value;
return -BLKID_ERR_CACHE;
}
(*value)++;
*end = '\0';
end++;
end = ++p;
} else {
end = skip_over_word(*value);
if (*end) {

View File

@ -26,6 +26,21 @@
#include "blkidP.h"
static void save_quoted(const char *data, FILE *file)
{
const char *p;
fputc('"', file);
for (p = data; p && *p; p++) {
if ((unsigned char) *p == 0x22 || /* " */
(unsigned char) *p == 0x5c) /* \ */
fputc('\\', file);
fputc(*p, file);
}
fputc('"', file);
}
static int save_dev(blkid_dev dev, FILE *file)
{
struct list_head *p;
@ -43,9 +58,14 @@ static int save_dev(blkid_dev dev, FILE *file)
if (dev->bid_pri)
fprintf(file, " PRI=\"%d\"", dev->bid_pri);
list_for_each(p, &dev->bid_tags) {
blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags);
fprintf(file, " %s=\"%s\"", tag->bit_name,tag->bit_val);
fputc(' ', file); /* space between tags */
fputs(tag->bit_name, file); /* tag NAME */
fputc('=', file); /* separator between NAME and VALUE */
save_quoted(tag->bit_val, file); /* tag "VALUE" */
}
fprintf(file, ">%s</device>\n", dev->bid_name);

View File

@ -200,7 +200,10 @@ partitions. This output format is \fBDEPRECATED\fR.
.TP
.B export
print key=value pairs for easy import into the environment; this output format
is automatically enabled when I/O Limits (\fB-i\fR option) are requested
is automatically enabled when I/O Limits (\fB-i\fR option) are requested.
The non-printing characters are encoded by ^ and M- notation and all
potentially unsafe characters are escaped.
.RE
.TP
.BI \-O " offset"

View File

@ -306,7 +306,7 @@ static void print_value(int output, int num, const char *devname,
printf("DEVNAME=%s\n", devname);
fputs(name, stdout);
fputs("=", stdout);
safe_print(value, valsz, NULL);
safe_print(value, valsz, " \\\"'$`<>");
fputs("\n", stdout);
} else {
@ -315,7 +315,7 @@ static void print_value(int output, int num, const char *devname,
fputs(" ", stdout);
fputs(name, stdout);
fputs("=\"", stdout);
safe_print(value, valsz, "\"");
safe_print(value, valsz, "\"\\");
fputs("\"", stdout);
}
}