mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-02-04 17:47:03 +03:00
add uft8 validation for safe volume label exporting
We will not support any other character encoding than plain ascii or utf8 for volume labels. All invalid utf8 and non-ascii characters are substituted for security reasons. No options, no fancy heuristics. Signed-off-by: Kay Sievers <kay.sievers@suse.de>
This commit is contained in:
parent
7104c558da
commit
5b13ecb830
1
Makefile
1
Makefile
@ -147,6 +147,7 @@ UDEV_OBJS = \
|
||||
udev_rules.o \
|
||||
udev_rules_parse.o \
|
||||
udev_utils.o \
|
||||
udev_utils_string.o \
|
||||
udev_utils_file.o \
|
||||
udev_utils_run.o \
|
||||
udev_libc_wrapper.o
|
||||
|
84
udev_utils.c
84
udev_utils.c
@ -36,69 +36,6 @@
|
||||
#include "udev_utils.h"
|
||||
#include "list.h"
|
||||
|
||||
/* compare string with pattern (supports * ? [0-9] [!A-Z]) */
|
||||
int strcmp_pattern(const char *p, const char *s)
|
||||
{
|
||||
if (s[0] == '\0') {
|
||||
while (p[0] == '*')
|
||||
p++;
|
||||
return (p[0] != '\0');
|
||||
}
|
||||
switch (p[0]) {
|
||||
case '[':
|
||||
{
|
||||
int not = 0;
|
||||
p++;
|
||||
if (p[0] == '!') {
|
||||
not = 1;
|
||||
p++;
|
||||
}
|
||||
while ((p[0] != '\0') && (p[0] != ']')) {
|
||||
int match = 0;
|
||||
if (p[1] == '-') {
|
||||
if ((s[0] >= p[0]) && (s[0] <= p[2]))
|
||||
match = 1;
|
||||
p += 3;
|
||||
} else {
|
||||
match = (p[0] == s[0]);
|
||||
p++;
|
||||
}
|
||||
if (match ^ not) {
|
||||
while ((p[0] != '\0') && (p[0] != ']'))
|
||||
p++;
|
||||
if (p[0] == ']')
|
||||
return strcmp_pattern(p+1, s+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '*':
|
||||
if (strcmp_pattern(p, s+1))
|
||||
return strcmp_pattern(p+1, s);
|
||||
return 0;
|
||||
case '\0':
|
||||
if (s[0] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ((p[0] == s[0]) || (p[0] == '?'))
|
||||
return strcmp_pattern(p+1, s+1);
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int string_is_true(const char *str)
|
||||
{
|
||||
if (strcasecmp(str, "true") == 0)
|
||||
return 1;
|
||||
if (strcasecmp(str, "yes") == 0)
|
||||
return 1;
|
||||
if (strcasecmp(str, "1") == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int log_priority(const char *priority)
|
||||
{
|
||||
@ -143,27 +80,6 @@ int kernel_release_satisfactory(unsigned int version, unsigned int patchlevel, u
|
||||
return 0;
|
||||
}
|
||||
|
||||
void replace_untrusted_chars(char *string)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
for (len = 0; string[len] != '\0'; len++) {
|
||||
if (strchr(";,~\\()\'", string[len])) {
|
||||
info("replace '%c' in '%s'", string[len], string);
|
||||
string[len] = '_';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void remove_trailing_char(char *path, char c)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
len = strlen(path);
|
||||
while (len > 0 && path[len-1] == c)
|
||||
path[--len] = '\0';
|
||||
}
|
||||
|
||||
int name_list_add(struct list_head *name_list, const char *name, int sort)
|
||||
{
|
||||
struct name_entry *loop_name;
|
||||
|
29
udev_utils.h
29
udev_utils.h
@ -31,22 +31,29 @@ struct name_entry {
|
||||
char name[PATH_SIZE];
|
||||
};
|
||||
|
||||
extern int strcmp_pattern(const char *p, const char *s);
|
||||
extern int kernel_release_satisfactory(unsigned int version, unsigned int patchlevel, unsigned int sublevel);
|
||||
extern int create_path(const char *path);
|
||||
/* udev_utils.c */
|
||||
extern int log_priority(const char *priority);
|
||||
extern int string_is_true(const char *str);
|
||||
extern int parse_get_pair(char **orig_string, char **left, char **right);
|
||||
extern int unlink_secure(const char *filename);
|
||||
extern int file_map(const char *filename, char **buf, size_t *bufsize);
|
||||
extern void file_unmap(void *buf, size_t bufsize);
|
||||
extern size_t buf_get_line(const char *buf, size_t buflen, size_t cur);
|
||||
extern void remove_trailing_char(char *path, char c);
|
||||
extern void replace_untrusted_chars(char *string);
|
||||
extern int kernel_release_satisfactory(unsigned int version, unsigned int patchlevel, unsigned int sublevel);
|
||||
extern int name_list_add(struct list_head *name_list, const char *name, int sort);
|
||||
extern int name_list_key_add(struct list_head *name_list, const char *key, const char *value);
|
||||
extern void name_list_cleanup(struct list_head *name_list);
|
||||
extern int add_matching_files(struct list_head *name_list, const char *dirname, const char *suffix);
|
||||
|
||||
/* udev_utils_string.c */
|
||||
extern int strcmp_pattern(const char *p, const char *s);
|
||||
extern int string_is_true(const char *str);
|
||||
extern void replace_untrusted_chars(char *string);
|
||||
extern void remove_trailing_char(char *path, char c);
|
||||
extern int utf8_encoded_valid_unichar(const char *str);
|
||||
|
||||
/* udev_utils_file.c */
|
||||
extern int create_path(const char *path);
|
||||
extern int file_map(const char *filename, char **buf, size_t *bufsize);
|
||||
extern void file_unmap(void *buf, size_t bufsize);
|
||||
extern int unlink_secure(const char *filename);
|
||||
extern size_t buf_get_line(const char *buf, size_t buflen, size_t cur);
|
||||
|
||||
/* udev_utils_run.c */
|
||||
extern int pass_env_to_socket(const char *name, const char *devpath, const char *action);
|
||||
extern int run_program(const char *command, const char *subsystem,
|
||||
char *result, size_t ressize, size_t *reslen, int log);
|
||||
|
243
udev_utils_string.c
Normal file
243
udev_utils_string.c
Normal file
@ -0,0 +1,243 @@
|
||||
/*
|
||||
* udev_utils_string.c - string manipulation
|
||||
*
|
||||
* Copyright (C) 2004-2005 Kay Sievers <kay.sievers@vrfy.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include "udev_libc_wrapper.h"
|
||||
#include "udev.h"
|
||||
#include "logging.h"
|
||||
#include "udev_utils.h"
|
||||
#include "list.h"
|
||||
|
||||
/* compare string with pattern (like fnmatch(), supports * ? [0-9] [!A-Z]) */
|
||||
int strcmp_pattern(const char *p, const char *s)
|
||||
{
|
||||
if (s[0] == '\0') {
|
||||
while (p[0] == '*')
|
||||
p++;
|
||||
return (p[0] != '\0');
|
||||
}
|
||||
switch (p[0]) {
|
||||
case '[':
|
||||
{
|
||||
int not = 0;
|
||||
p++;
|
||||
if (p[0] == '!') {
|
||||
not = 1;
|
||||
p++;
|
||||
}
|
||||
while ((p[0] != '\0') && (p[0] != ']')) {
|
||||
int match = 0;
|
||||
if (p[1] == '-') {
|
||||
if ((s[0] >= p[0]) && (s[0] <= p[2]))
|
||||
match = 1;
|
||||
p += 3;
|
||||
} else {
|
||||
match = (p[0] == s[0]);
|
||||
p++;
|
||||
}
|
||||
if (match ^ not) {
|
||||
while ((p[0] != '\0') && (p[0] != ']'))
|
||||
p++;
|
||||
if (p[0] == ']')
|
||||
return strcmp_pattern(p+1, s+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '*':
|
||||
if (strcmp_pattern(p, s+1))
|
||||
return strcmp_pattern(p+1, s);
|
||||
return 0;
|
||||
case '\0':
|
||||
if (s[0] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ((p[0] == s[0]) || (p[0] == '?'))
|
||||
return strcmp_pattern(p+1, s+1);
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int string_is_true(const char *str)
|
||||
{
|
||||
if (strcasecmp(str, "true") == 0)
|
||||
return 1;
|
||||
if (strcasecmp(str, "yes") == 0)
|
||||
return 1;
|
||||
if (strcasecmp(str, "1") == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void remove_trailing_char(char *path, char c)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
len = strlen(path);
|
||||
while (len > 0 && path[len-1] == c)
|
||||
path[--len] = '\0';
|
||||
}
|
||||
|
||||
/* count of characters used to encode one unicode char */
|
||||
static int utf8_encoded_expected_len(const char *str)
|
||||
{
|
||||
unsigned char c = (unsigned char)str[0];
|
||||
|
||||
if (c < 0x80)
|
||||
return 1;
|
||||
if ((c & 0xe0) == 0xc0)
|
||||
return 2;
|
||||
if ((c & 0xf0) == 0xe0)
|
||||
return 3;
|
||||
if ((c & 0xf8) == 0xf0)
|
||||
return 4;
|
||||
if ((c & 0xfc) == 0xf8)
|
||||
return 5;
|
||||
if ((c & 0xfe) == 0xfc)
|
||||
return 6;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* decode one unicode char */
|
||||
static int utf8_encoded_to_unichar(const char *str)
|
||||
{
|
||||
int unichar;
|
||||
int len;
|
||||
int i;
|
||||
|
||||
len = utf8_encoded_expected_len(str);
|
||||
switch (len) {
|
||||
case 1:
|
||||
return (int)str[0];
|
||||
case 2:
|
||||
unichar = str[0] & 0x1f;
|
||||
break;
|
||||
case 3:
|
||||
unichar = (int)str[0] & 0x0f;
|
||||
break;
|
||||
case 4:
|
||||
unichar = (int)str[0] & 0x07;
|
||||
break;
|
||||
case 5:
|
||||
unichar = (int)str[0] & 0x03;
|
||||
break;
|
||||
case 6:
|
||||
unichar = (int)str[0] & 0x01;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 1; i < len; i++) {
|
||||
if (((int)str[i] & 0xc0) != 0x80)
|
||||
return -1;
|
||||
unichar <<= 6;
|
||||
unichar |= (int)str[i] & 0x3f;
|
||||
}
|
||||
|
||||
return unichar;
|
||||
}
|
||||
|
||||
/* expected size used to encode one unicode char */
|
||||
static int utf8_unichar_to_encoded_len(int unichar)
|
||||
{
|
||||
if (unichar < 0x80)
|
||||
return 1;
|
||||
if (unichar < 0x800)
|
||||
return 2;
|
||||
if (unichar < 0x10000)
|
||||
return 3;
|
||||
if (unichar < 0x200000)
|
||||
return 4;
|
||||
if (unichar < 0x4000000)
|
||||
return 5;
|
||||
return 6;
|
||||
}
|
||||
|
||||
/* check if unicode char has a valid numeric range */
|
||||
static int utf8_unichar_valid_range(int unichar)
|
||||
{
|
||||
if (unichar > 0x10ffff)
|
||||
return 0;
|
||||
if ((unichar & 0xfffff800) == 0xd800)
|
||||
return 0;
|
||||
if ((unichar > 0xfdcf) && (unichar < 0xfdf0))
|
||||
return 0;
|
||||
if ((unichar & 0xffff) == 0xffff)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* validate one encoded unicode char and return its length */
|
||||
int utf8_encoded_valid_unichar(const char *str)
|
||||
{
|
||||
int len;
|
||||
int unichar;
|
||||
int i;
|
||||
|
||||
len = utf8_encoded_expected_len(str);
|
||||
if (len == 0)
|
||||
return -1;
|
||||
|
||||
/* ascii is valid */
|
||||
if (len == 1)
|
||||
return 1;
|
||||
|
||||
/* check if expected encoded chars are available */
|
||||
for (i = 0; i < len; i++)
|
||||
if ((str[i] & 0x80) != 0x80)
|
||||
return -1;
|
||||
|
||||
unichar = utf8_encoded_to_unichar(str);
|
||||
|
||||
/* check if encoded length matches encoded value */
|
||||
if (utf8_unichar_to_encoded_len(unichar) != len)
|
||||
return -1;
|
||||
|
||||
/* check if value has valid range */
|
||||
if (!utf8_unichar_valid_range(unichar))
|
||||
return -1;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void replace_untrusted_chars(char *string)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
for (len = 0; string[len] != '\0'; len++) {
|
||||
if (strchr(";,~\\()\'", string[len]))
|
||||
string[len] = '_';
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user