2
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
3
* NOVELL (All rights reserved)
5
* This program is free software; you can redistribute it and/or
6
* modify it under the terms of version 2 of the GNU General Public
7
* License published by the Free Software Foundation.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, contact Novell, Inc.
18
/* assistance routines */
27
#define _(s) gettext(s)
28
#include <netinet/in.h>
29
#include <linux/socket.h>
30
#include <linux/limits.h>
31
#include <arpa/inet.h>
32
#include <linux/capability.h>
33
#include <sys/types.h>
39
#include "parser_yacc.h"
45
#define PDEBUG(fmt, args...) printf("Lexer: " fmt, ## args)
48
#define PDEBUG(fmt, args...) /* Do nothing */
50
#define NPDEBUG(fmt, args...) /* Do nothing */
52
struct keyword_table {
57
static struct keyword_table keyword_table[] = {
59
{"network", TOK_NETWORK},
61
{"capability", TOK_CAPABILITY},
65
{"defined", TOK_DEFINED},
66
{"change_profile", TOK_CHANGE_PROFILE},
67
{"unsafe", TOK_UNSAFE},
73
{"subset", TOK_SUBSET},
77
{"rlimit", TOK_RLIMIT},
79
{"rewrite", TOK_ALIAS},
80
{"ptrace", TOK_PTRACE},
83
{"remount", TOK_REMOUNT},
84
{"umount", TOK_UMOUNT},
85
{"unmount", TOK_UMOUNT},
86
{"pivot_root", TOK_PIVOTROOT},
91
static struct keyword_table rlimit_table[] = {
93
{"fsize", RLIMIT_FSIZE},
94
{"data", RLIMIT_DATA},
95
{"stack", RLIMIT_STACK},
96
{"core", RLIMIT_CORE},
98
{"nofile", RLIMIT_NOFILE},
99
{"ofile", RLIMIT_OFILE},
101
{"nproc", RLIMIT_NPROC},
102
{"memlock", RLIMIT_MEMLOCK},
103
{"locks", RLIMIT_LOCKS},
104
{"sigpending", RLIMIT_SIGPENDING},
105
{"msgqueue", RLIMIT_MSGQUEUE},
107
{"nice", RLIMIT_NICE},
110
{"rtprio", RLIMIT_RTPRIO},
116
/* for alpha matches, check for keywords */
117
static int get_table_token(const char *name __unused, struct keyword_table *table,
122
for (i = 0; table[i].keyword; i++) {
123
PDEBUG("Checking %s %s\n", name, table[i].keyword);
124
if (strcmp(keyword, table[i].keyword) == 0) {
125
PDEBUG("Found %s %s\n", name, table[i].keyword);
126
return table[i].token;
130
PDEBUG("Unable to find %s %s\n", name, keyword);
134
static struct keyword_table capability_table[] = {
136
#include "cap_names.h"
144
/* for alpha matches, check for keywords */
145
int get_keyword_token(const char *keyword)
147
return get_table_token("keyword", keyword_table, keyword);
150
int name_to_capability(const char *keyword)
152
return get_table_token("capability", capability_table, keyword);
155
int get_rlimit(const char *name)
157
return get_table_token("rlimit", rlimit_table, name);
160
struct network_tuple {
166
unsigned int protocol;
169
/* FIXME: currently just treating as a bit mask this will have to change
170
* set up a table of mappings, there can be several mappings for a
172
* currently the mapping does not set the protocol for stream/dgram to
173
* anything other than 0.
174
* network inet tcp -> network inet stream 0 instead of
175
* network inet raw tcp.
176
* some entries are just provided for completeness at this time
178
/* values stolen from /etc/protocols - needs to change */
182
#define RAW_ICMPv6 58
184
/* used by af_name.h to auto generate table entries for "name", AF_NAME
186
#define AA_GEN_NET_ENT(name, AF) {name, AF, "stream", SOCK_STREAM, "", 0xffffff}, {name, AF, "dgram", SOCK_DGRAM, "", 0xffffff}, {name, AF, "seqpacket", SOCK_SEQPACKET, "", 0xffffff}, {name, AF, "rdm", SOCK_RDM, "", 0xffffff}, {name, AF, "raw", SOCK_RAW, "", 0xffffff}, {name, AF, "packet", SOCK_PACKET, "", 0xffffff},
187
/*FIXME: missing {name, AF, "dccp", SOCK_DCCP, "", 0xfffffff}, */
189
static struct network_tuple network_mappings[] = {
191
#include "af_names.h"
192
/* FIXME: af_names.h is missing AF_LLC, AF_TIPC */
194
{"inet", AF_INET, "raw", SOCK_RAW,
195
"tcp", 1 << RAW_TCP},
196
{"inet", AF_INET, "raw", SOCK_RAW,
197
"udp", 1 << RAW_UDP},
198
{"inet", AF_INET, "raw", SOCK_RAW,
199
"icmp", 1 << RAW_ICMP},
200
{"inet", AF_INET, "tcp", SOCK_STREAM,
201
"", 0xffffffff}, /* should we give raw tcp too? */
202
{"inet", AF_INET, "udp", SOCK_DGRAM,
203
"", 0xffffffff}, /* should these be open masks? */
204
{"inet", AF_INET, "icmp", SOCK_RAW,
206
{"inet6", AF_INET6, "tcp", SOCK_STREAM,
208
{"inet6", AF_INET6, "udp", SOCK_DGRAM,
210
/* what do we do with icmp on inet6?
211
{"inet6", AF_INET, "icmp", SOCK_RAW, 0},
212
{"inet6", AF_INET, "icmpv6", SOCK_RAW, 0},
215
{NULL, 0, NULL, 0, NULL, 0}
218
/* The apparmor kernel patches up until 2.6.38 didn't handle networking
219
* tables with sizes > AF_MAX correctly. This could happen when the
220
* parser was built against newer kernel headers and then used to load
221
* policy on an older kernel. This could happen during upgrades or
222
* in multi-kernel boot systems.
224
* Try to detect the running kernel version and use that to determine
227
#define PROC_VERSION "/proc/sys/kernel/osrelease"
228
static size_t kernel_af_max(void) {
233
if (!net_af_max_override) {
236
/* the override parameter is specifying the max value */
237
if (net_af_max_override > 0)
238
return net_af_max_override;
240
fd = open(PROC_VERSION, O_RDONLY);
242
/* fall back to default provided during build */
244
res = read(fd, &buffer, sizeof(buffer));
248
buffer[sizeof(buffer)-1] = '\0';
249
res = sscanf(buffer, "2.6.%d", &major);
273
/* kernels .38 and later should handle this correctly so no
274
* static mapping needed
281
/* Yuck. We grab AF_* values to define above from linux/socket.h because
282
* they are more accurate than sys/socket.h for what the kernel actually
283
* supports. However, we can't just include linux/socket.h directly,
284
* because the AF_* definitions are protected with an ifdef KERNEL
285
* wrapper, but we don't want to define that because that can cause
286
* other redefinitions from glibc. However, because the kernel may have
287
* more definitions than glibc, we need make sure AF_MAX reflects this,
288
* hence the wrapping function.
290
size_t get_af_max() {
292
/* HACK: declare that version without "create" had a static AF_MAX */
293
if (!perms_create && !net_af_max_override)
294
net_af_max_override = -1;
296
#if AA_AF_MAX > AF_MAX
302
/* HACK: some kernels didn't handle network tables from parsers
303
* compiled against newer kernel headers as they are larger than
304
* the running kernel expected. If net_override is defined check
305
* to see if there is a static max specified for that kernel
307
if (net_af_max_override) {
308
size_t max = kernel_af_max();
309
if (max && max < af_max)
315
struct aa_network_entry *new_network_ent(unsigned int family,
317
unsigned int protocol)
319
struct aa_network_entry *new_entry;
320
new_entry = calloc(1, sizeof(struct aa_network_entry));
322
new_entry->family = family;
323
new_entry->type = type;
324
new_entry->protocol = protocol;
325
new_entry->next = NULL;
330
struct aa_network_entry *network_entry(const char *family, const char *type,
331
const char *protocol)
334
struct aa_network_entry *new_entry, *entry = NULL;
336
for (i = 0; network_mappings[i].family_name; i++) {
338
PDEBUG("Checking family %s\n", network_mappings[i].family_name);
339
if (strcmp(family, network_mappings[i].family_name) != 0)
341
PDEBUG("Found family %s\n", family);
344
PDEBUG("Checking type %s\n", network_mappings[i].type_name);
345
if (strcmp(type, network_mappings[i].type_name) != 0)
347
PDEBUG("Found type %s\n", type);
350
PDEBUG("Checking protocol type %s\n", network_mappings[i].protocol_name);
351
if (strcmp(type, network_mappings[i].protocol_name) != 0)
353
/* fixme should we allow specifying protocol by #
354
* without needing the protocol mapping? */
356
/* if here we have a match */
357
new_entry = new_network_ent(network_mappings[i].family,
358
network_mappings[i].type,
359
network_mappings[i].protocol);
361
yyerror(_("Memory allocation error."));
362
new_entry->next = entry;
369
char *processunquoted(char *string, int len)
374
tmp = (char *)malloc(len + 1);
379
for (l = 0; l < len; l++) {
380
if (string[l] == '\\' && l < len - 3) {
381
if (strchr("0123", string[l + 1]) &&
382
strchr("0123456789", string[l + 2]) &&
383
strchr("0123456789", string[l + 3])) {
384
/* three digit octal */
385
int res = (string[l + 1] - '0') * 64 +
386
(string[l + 2] - '0') * 8 +
387
(string[l + 3] - '0');
405
char *processid(char *string, int len)
407
/* lexer should never call this fn if len <= 0 */
411
return processquoted(string, len);
412
return processunquoted(string, len);
415
/* rewrite a quoted string substituting escaped characters for the
416
* real thing. Strip the quotes around the string */
418
char *processquoted(char *string, int len)
422
/* the result string will be shorter or equal in length */
423
tmp = (char *)malloc(len + 1);
428
for (l = 1; l < len - 1; l++) {
429
if (string[l] == '\\' && l < len - 2) {
430
switch (string[l + 1]) {
451
case '0': case '1': case '2': case '3':
453
strchr("0123456789", string[l + 2]) &&
454
strchr("0123456789", string[l + 3])) {
455
/* three digit octal */
456
int res = (string[l + 1] - '0') * 64 +
457
(string[l + 2] - '0') * 8 +
458
(string[l + 3] - '0');
465
/* any unsupported escape sequence results in all
466
chars being copied. */
481
/* strip off surrounding delimiters around variables */
482
char *process_var(const char *var)
484
const char *orig = var;
485
int len = strlen(var);
487
if (*orig == '@' || *orig == '$') {
491
PERROR("ASSERT: Found var '%s' without variable prefix\n",
499
if (orig[len - 1] != '}') {
500
PERROR("ASSERT: No matching '}' in variable '%s'\n",
507
return strndup(orig, len);
510
/* returns -1 if value != true or false, otherwise 0 == false, 1 == true */
511
int str_to_boolean(const char *value)
515
if (strcasecmp("TRUE", value) == 0)
517
if (strcasecmp("FALSE", value) == 0)
522
static int warned_uppercase = 0;
524
static void warn_uppercase(void)
526
if (!warned_uppercase) {
527
pwarn(_("Uppercase qualifiers \"RWLIMX\" are deprecated, please convert to lowercase\n"
528
"See the apparmor.d(5) manpage for details.\n"));
529
warned_uppercase = 1;
533
static int parse_sub_mode(const char *str_mode, const char *mode_desc __unused)
536
#define IS_DIFF_QUAL(mode, q) (((mode) & AA_MAY_EXEC) && (((mode) & AA_EXEC_TYPE) != ((q) & AA_EXEC_TYPE)))
541
PDEBUG("Parsing mode: %s\n", str_mode);
549
char next = *(p + 1);
556
if (read_implies_exec) {
557
PDEBUG("Parsing mode: found %s READ imply X\n", mode_desc);
558
mode |= AA_MAY_READ | AA_EXEC_MMAP;
560
PDEBUG("Parsing mode: found %s READ\n", mode_desc);
566
PDEBUG("Parsing mode: found %s WRITE\n", mode_desc);
567
if ((mode & AA_MAY_APPEND) && !(mode & AA_MAY_WRITE))
568
yyerror(_("Conflict 'a' and 'w' perms are mutually exclusive."));
569
mode |= AA_MAY_WRITE | AA_MAY_APPEND;
572
case COD_APPEND_CHAR:
573
PDEBUG("Parsing mode: found %s APPEND\n", mode_desc);
574
if (mode & AA_MAY_WRITE)
575
yyerror(_("Conflict 'a' and 'w' perms are mutually exclusive."));
576
mode |= AA_MAY_APPEND;
580
PDEBUG("Parsing mode: found %s LINK\n", mode_desc);
585
PDEBUG("Parsing mode: found %s LOCK\n", mode_desc);
589
case COD_INHERIT_CHAR:
590
PDEBUG("Parsing mode: found INHERIT\n");
591
if (mode & AA_EXEC_MODIFIERS) {
592
yyerror(_("Exec qualifier 'i' invalid, conflicting qualifier already specified"));
594
if (next != tolower(next))
596
mode |= (AA_EXEC_INHERIT | AA_MAY_EXEC);
601
case COD_UNSAFE_UNCONFINED_CHAR:
602
tmode = AA_EXEC_UNSAFE;
603
pwarn(_("Unconfined exec qualifier (%c%c) allows some dangerous environment variables "
604
"to be passed to the unconfined process; 'man 5 apparmor.d' for details.\n"),
605
COD_UNSAFE_UNCONFINED_CHAR, COD_EXEC_CHAR);
607
case COD_UNCONFINED_CHAR:
608
tmode |= AA_EXEC_UNCONFINED | AA_MAY_EXEC;
609
PDEBUG("Parsing mode: found UNCONFINED\n");
610
if (IS_DIFF_QUAL(mode, tmode)) {
611
yyerror(_("Exec qualifier '%c' invalid, conflicting qualifier already specified"),
614
if (next != tolower(next))
622
case COD_UNSAFE_PROFILE_CHAR:
623
case COD_UNSAFE_LOCAL_CHAR:
624
tmode = AA_EXEC_UNSAFE;
626
case COD_PROFILE_CHAR:
628
if (tolower(this) == COD_UNSAFE_PROFILE_CHAR)
629
tmode |= AA_EXEC_PROFILE | AA_MAY_EXEC;
632
tmode |= AA_EXEC_LOCAL | AA_MAY_EXEC;
634
PDEBUG("Parsing mode: found PROFILE\n");
635
if (tolower(next) == COD_INHERIT_CHAR) {
636
tmode |= AA_EXEC_INHERIT;
637
if (IS_DIFF_QUAL(mode, tmode)) {
638
yyerror(_("Exec qualifier '%c%c' invalid, conflicting qualifier already specified"), this, next);
643
} else if (tolower(next) == COD_UNSAFE_UNCONFINED_CHAR) {
644
tmode |= AA_EXEC_PUX;
645
if (IS_DIFF_QUAL(mode, tmode)) {
646
yyerror(_("Exec qualifier '%c%c' invalid, conflicting qualifier already specified"), this, next);
651
} else if (IS_DIFF_QUAL(mode, tmode)) {
652
yyerror(_("Exec qualifier '%c' invalid, conflicting qualifier already specified"), this);
655
if (next != tolower(next))
664
PDEBUG("Parsing mode: found %s MMAP\n", mode_desc);
665
mode |= AA_EXEC_MMAP;
669
/* this is valid for deny rules, and named transitions
670
* but invalid for regular x transitions
679
lower = tolower(this);
683
case COD_APPEND_CHAR:
685
case COD_INHERIT_CHAR:
688
PDEBUG("Parsing mode: found invalid upper case char %c\n", this);
694
yyerror(_("Internal: unexpected mode character '%c' in input"),
704
PDEBUG("Parsed mode: %s 0x%x\n", str_mode, mode);
709
int parse_mode(const char *str_mode)
712
tmp = parse_sub_mode(str_mode, "");
713
mode = SHIFT_MODE(tmp, AA_USER_SHIFT);
714
mode |= SHIFT_MODE(tmp, AA_OTHER_SHIFT);
715
if (mode & ~AA_VALID_PERMS)
716
yyerror(_("Internal error generated invalid perm 0x%llx\n"), mode);
720
struct cod_entry *new_entry(char *namespace, char *id, int mode, char *link_id)
722
struct cod_entry *entry = NULL;
724
entry = (struct cod_entry *)calloc(1, sizeof(struct cod_entry));
728
entry->namespace = namespace;
730
entry->link_name = link_id;
735
entry->pattern_type = ePatternInvalid;
736
entry->pat.regex = NULL;
740
PDEBUG(" Insertion of: (%s)\n", entry->name);
744
struct cod_entry *copy_cod_entry(struct cod_entry *orig)
746
struct cod_entry *entry = NULL;
748
entry = (struct cod_entry *)calloc(1, sizeof(struct cod_entry));
752
entry->namespace = orig->namespace ? strdup(orig->namespace) : NULL;
753
entry->name = strdup(orig->name);
754
entry->link_name = orig->link_name ? strdup(orig->link_name) : NULL;
755
entry->mode = orig->mode;
756
entry->audit = orig->audit;
757
entry->deny = orig->deny;
759
/* XXX - need to create copies of the patterns, too */
760
entry->pattern_type = orig->pattern_type;
761
entry->pat.regex = NULL;
763
entry->next = orig->next;
768
void free_cod_entries(struct cod_entry *list)
773
free_cod_entries(list->next);
775
free(list->namespace);
779
free(list->link_name);
781
free(list->pat.regex);
785
void free_mnt_entries(struct mnt_entry *list)
790
free_mnt_entries(list->next);
791
free(list->mnt_point);
793
free_value_list(list->dev_type);
794
free_value_list(list->opts);
799
static void debug_base_perm_mask(int mask)
801
if (HAS_MAY_READ(mask))
802
printf("%c", COD_READ_CHAR);
803
if (HAS_MAY_WRITE(mask))
804
printf("%c", COD_WRITE_CHAR);
805
if (HAS_MAY_APPEND(mask))
806
printf("%c", COD_APPEND_CHAR);
807
if (HAS_MAY_LINK(mask))
808
printf("%c", COD_LINK_CHAR);
809
if (HAS_MAY_LOCK(mask))
810
printf("%c", COD_LOCK_CHAR);
811
if (HAS_EXEC_MMAP(mask))
812
printf("%c", COD_MMAP_CHAR);
813
if (HAS_MAY_EXEC(mask))
814
printf("%c", COD_EXEC_CHAR);
817
void debug_cod_entries(struct cod_entry *list)
819
struct cod_entry *item = NULL;
821
printf("--- Entries ---\n");
823
list_for_each(list, item) {
825
printf("Item is NULL!\n");
828
if (HAS_CHANGE_PROFILE(item->mode))
829
printf(" change_profile");
830
if (HAS_EXEC_UNSAFE(item->mode))
832
debug_base_perm_mask(SHIFT_TO_BASE(item->mode, AA_USER_SHIFT));
834
debug_base_perm_mask(SHIFT_TO_BASE(item->mode, AA_OTHER_SHIFT));
836
printf("\tName:\t(%s)\n", item->name);
838
printf("\tName:\tNULL\n");
841
printf("\tNamespace:\t(%s)\n", item->namespace);
843
if (AA_LINK_BITS & item->mode)
844
printf("\tlink:\t(%s)\n", item->link_name ? item->link_name : "/**");
849
void debug_flags(struct codomain *cod)
851
printf("Profile Mode:\t");
853
if (cod->flags.complain)
858
if (cod->flags.audit)
867
static const char *capnames[] = {
904
const char *capability_to_name(unsigned int cap)
908
capname = (cap < (sizeof(capnames) / sizeof(char *))
909
? capnames[cap] : "invalid-capability");
914
void __debug_capabilities(uint64_t capset, const char *name)
919
for (i = 0; i < (sizeof(capnames)/sizeof(char *)); i++) {
920
if (((1ull << i) & capset) != 0) {
921
printf (" %s", capability_to_name(i));
926
void debug_capabilities(struct codomain *cod)
928
if (cod->capabilities != 0ull)
929
__debug_capabilities(cod->capabilities, "Capabilities");
930
if (cod->audit_caps != 0ull)
931
__debug_capabilities(cod->audit_caps, "Audit Caps");
932
if (cod->deny_caps != 0ull)
933
__debug_capabilities(cod->deny_caps, "Deny Caps");
934
if (cod->quiet_caps != 0ull)
935
__debug_capabilities(cod->quiet_caps, "Quiet Caps");
938
void debug_cod_list(struct codomain *cod)
941
printf("Namespace:\t\t%s\n", cod->namespace);
944
printf("Name:\t\t%s\n", cod->name);
946
printf("Name:\t\tNULL\n");
949
printf("Local To:\t%s\n", cod->parent->name);
953
debug_capabilities(cod);
956
debug_cod_entries(cod->entries);
959
dump_policy_hats(cod);
962
struct value_list *new_value_list(char *value)
964
struct value_list *val = calloc(1, sizeof(struct value_list));
970
void free_value_list(struct value_list *list)
972
struct value_list *next;
983
struct value_list *dup_value_list(struct value_list *list)
985
struct value_list *entry, *dup, *head = NULL;
988
list_for_each(list, entry) {
991
value = strdup(list->value);
995
dup = new_value_list(value);
999
list_append(head, dup);
1009
free_value_list(head);
1014
void print_value_list(struct value_list *list)
1016
struct value_list *entry;
1021
fprintf(stderr, "%s", list->value);
1023
list_for_each(list, entry) {
1024
fprintf(stderr, ", %s", entry->value);
1028
struct cond_entry *new_cond_entry(char *name, struct value_list *list)
1030
struct cond_entry *ent = calloc(1, sizeof(struct cond_entry));
1039
void free_cond_entry(struct cond_entry *ent)
1043
free_value_list(ent->vals);
1048
void print_cond_entry(struct cond_entry *ent)
1051
fprintf(stderr, "%s=(", ent->name);
1052
print_value_list(ent->vals);
1053
fprintf(stderr, ")\n");
1058
int test_str_to_boolean(void)
1063
retval = str_to_boolean("TRUE");
1064
MY_TEST(retval == 1, "str2bool for TRUE");
1066
retval = str_to_boolean("TrUe");
1067
MY_TEST(retval == 1, "str2bool for TrUe");
1069
retval = str_to_boolean("false");
1070
MY_TEST(retval == 0, "str2bool for false");
1072
retval = str_to_boolean("flase");
1073
MY_TEST(retval == -1, "str2bool for flase");
1078
int test_processunquoted(void)
1081
char *teststring, *processedstring;
1084
MY_TEST(strcmp(teststring, processunquoted(teststring, strlen(teststring))) == 0,
1085
"processunquoted on empty string");
1087
teststring = "123\\421123";
1088
MY_TEST(strcmp(teststring, processunquoted(teststring, strlen(teststring))) == 0,
1089
"processunquoted on invalid octal");
1091
/* Oh wow, our octal processing is busticated - FIXME
1092
teststring = "123\\109123";
1093
processedstring = "123\109123";
1094
MY_TEST(strcmp(processedstring, processunquoted(teststring, strlen(teststring))) == 0,
1095
"processunquoted on octal 10");
1097
teststring = "123\\189123";
1098
processedstring = "123\189123";
1099
MY_TEST(strcmp(processedstring, processunquoted(teststring, strlen(teststring))) == 0,
1100
"processunquoted on octal 10");
1106
int test_processquoted(void)
1109
char *teststring, *processedstring;
1113
out = processquoted(teststring, strlen(teststring));
1114
MY_TEST(strcmp(teststring, out) == 0,
1115
"processquoted on empty string");
1118
teststring = "\"abcdefg\"";
1119
processedstring = "abcdefg";
1120
out = processquoted(teststring, strlen(teststring));
1121
MY_TEST(strcmp(processedstring, out) == 0,
1122
"processquoted on simple string");
1125
teststring = "\"abcd\\tefg\"";
1126
processedstring = "abcd\tefg";
1127
out = processquoted(teststring, strlen(teststring));
1128
MY_TEST(strcmp(processedstring, out) == 0,
1129
"processquoted on string with tab");
1132
teststring = "\"abcdefg\\\"";
1133
processedstring = "abcdefg\\";
1134
out = processquoted(teststring, strlen(teststring));
1135
MY_TEST(strcmp(processedstring, out) == 0,
1136
"processquoted on trailing slash");
1139
teststring = "\"a\\\\bcdefg\"";
1140
processedstring = "a\\bcdefg";
1141
out = processquoted(teststring, strlen(teststring));
1142
MY_TEST(strcmp(processedstring, out) == 0,
1143
"processquoted on quoted slash");
1146
teststring = "\"a\\\"bcde\\\"fg\"";
1147
processedstring = "a\"bcde\"fg";
1148
out = processquoted(teststring, strlen(teststring));
1149
MY_TEST(strcmp(processedstring, out) == 0,
1150
"processquoted on quoted quotes");
1153
teststring = "\"\\rabcdefg\"";
1154
processedstring = "\rabcdefg";
1155
out = processquoted(teststring, strlen(teststring));
1156
MY_TEST(strcmp(processedstring, out) == 0,
1157
"processquoted on quoted \\r");
1160
teststring = "\"abcdefg\\n\"";
1161
processedstring = "abcdefg\n";
1162
out = processquoted(teststring, strlen(teststring));
1163
MY_TEST(strcmp(processedstring, out) == 0,
1164
"processquoted on quoted \\n");
1167
teststring = "\"\\Uabc\\Ndefg\\x\"";
1168
processedstring = "\\Uabc\\Ndefg\\x";
1169
out = processquoted(teststring, strlen(teststring));
1170
MY_TEST(strcmp(processedstring, out) == 0,
1171
"processquoted passthrough on invalid quoted chars");
1174
teststring = "\"abc\\042defg\"";
1175
processedstring = "abc\"defg";
1176
out = processquoted(teststring, strlen(teststring));
1177
MY_TEST(strcmp(processedstring, out) == 0,
1178
"processquoted on quoted octal \\042");
1181
teststring = "\"abcdefg\\176\"";
1182
processedstring = "abcdefg~";
1183
out = processquoted(teststring, strlen(teststring));
1184
MY_TEST(strcmp(processedstring, out) == 0,
1185
"processquoted on quoted octal \\176");
1188
/* yes, our octal processing is lame; patches accepted */
1189
teststring = "\"abc\\42defg\"";
1190
processedstring = "abc\\42defg";
1191
out = processquoted(teststring, strlen(teststring));
1192
MY_TEST(strcmp(processedstring, out) == 0,
1193
"processquoted passthrough quoted invalid octal \\42");
1196
teststring = "\"abcdefg\\04\"";
1197
processedstring = "abcdefg\\04";
1198
out = processquoted(teststring, strlen(teststring));
1199
MY_TEST(strcmp(processedstring, out) == 0,
1200
"processquoted passthrough quoted invalid trailing octal \\04");
1211
retval = test_str_to_boolean();
1215
retval = test_processunquoted();
1219
retval = test_processquoted();
1225
#endif /* UNIT_TEST */