diff --git a/src/asn1-parse.y b/src/asn1-parse.y index 3336c43..5bff15c 100755 --- a/src/asn1-parse.y +++ b/src/asn1-parse.y @@ -1,1046 +1,1050 @@ /* asn1-parse.y - ASN.1 grammar * Copyright (C) 2000, 2001 Fabio Fiorina * Copyright (C) 2001 Free Software Foundation, Inc. * Copyright (C) 2002, 2003, 2006, 2007, 2010, 2012 g10 Code GmbH * * This file is part of KSBA. * * KSBA is free software; you can redistribute it and/or modify * it under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * KSBA 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 copies of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, see . */ /*****************************************************/ /* File: x509_ASN.y */ /* Description: input file for 'bison' program. */ /* The output file is a parser (in C language) for */ /* ASN.1 syntax */ /*****************************************************/ %{ #ifndef BUILD_GENTOOLS # include #endif #include #include #include #include #include #include #ifdef BUILD_GENTOOLS # include "gen-help.h" #else # include "util.h" # include "ksba.h" #endif #include "asn1-func.h" /* It would be better to make yyparse static but there is no way to do this. Let's hope that this macros works. */ #define yyparse _ksba_asn1_yyparse /* #define YYDEBUG 1 */ #define MAX_STRING_LENGTH 129 /* Dummy print so that yytoknum will be defined. */ #define YYPRINT(F, N, L) do { } while (0); /* constants used in the grammar */ enum { CONST_EXPLICIT = 1, CONST_IMPLICIT }; struct parser_control_s { FILE *fp; int lineno; int debug; int result_parse; AsnNode parse_tree; AsnNode all_nodes; }; #define PARSECTL ((struct parser_control_s *)parm) %} %param {void *parm} %define api.pure full %define parse.error verbose -%expect 1 + //%expect 1 %union { unsigned int constant; char str[MAX_STRING_LENGTH]; AsnNode node; } %{ static AsnNode new_node (struct parser_control_s *parsectl, node_type_t type); #define NEW_NODE(a) (new_node (PARSECTL, (a))) static void set_name (AsnNode node, const char *name); static void set_str_value (AsnNode node, const char *text); static void set_ulong_value (AsnNode node, const char *text); static void set_right (AsnNode node, AsnNode right); static void append_right (AsnNode node, AsnNode right); static void set_down (AsnNode node, AsnNode down); static int yylex (YYSTYPE *lvalp, void *parm); static void yyerror (void *parm, const char *s); %} %token-table %token ASSIG "::=" %token NUM %token IDENTIFIER %token OPTIONAL "OPTIONAL" %token INTEGER "INTEGER" %token SIZE "SIZE" %token OCTET "OCTET" %token STRING "STRING" %token SEQUENCE "SEQUENCE" %token BIT "BIT" %token UNIVERSAL "UNIVERSAL" %token PRIVATE "PRIVATE" %token DEFAULT "DEFAULT" %token CHOICE "CHOICE" %token OF "OF" %token OBJECT "OBJECT" %token STR_IDENTIFIER "IDENTIFIER" %token ksba_BOOLEAN "BOOLEAN" %token ksba_TRUE "TRUE" %token ksba_FALSE "FALSE" %token APPLICATION "APPLICATION" %token ANY "ANY" %token DEFINED "DEFINED" %token SET "SET" %token BY "BY" %token EXPLICIT "EXPLICIT" %token IMPLICIT "IMPLICIT" %token DEFINITIONS "DEFINITIONS" %token TAGS "TAGS" %token ksba_BEGIN "BEGIN" %token ksba_END "END" %token UTCTime "UTCTime" %token GeneralizedTime "GeneralizedTime" %token FROM "FROM" %token IMPORTS "IMPORTS" %token TOKEN_NULL "NULL" %token ENUMERATED "ENUMERATED" %token UTF8STRING "UTF8String" %token NUMERICSTRING "NumericString" %token PRINTABLESTRING "PrintableString" %token TELETEXSTRING "TeletexString" %token IA5STRING "IA5String" %token UNIVERSALSTRING "UniversalString" %token BMPSTRING "BMPString" %type octet_string_def constant constant_list type_assig_right %type integer_def type_assig type_assig_list sequence_def type_def %type bit_string_def default size_def choise_def object_def %type boolean_def any_def size_def2 obj_constant obj_constant_list %type constant_def type_constant type_constant_list definitions %type definitions_id Time bit_element bit_element_list set_def -%type identifier_list imports_def tag_type tag type_assig_right_tag +%type /* identifier_list */ imports_def tag_type tag type_assig_right_tag %type type_assig_right_tag_default enumerated_def string_def %type utf8_string_def numeric_string_def printable_string_def %type teletex_string_def ia5_string_def universal_string_def %type bmp_string_def %type pos_num neg_num pos_neg_num pos_neg_identifier num_identifier %type class explicit_implicit %% input: /* empty */ | input definitions ; pos_num : NUM { strcpy($$,$1); } | '+' NUM { strcpy($$,$2); } ; neg_num : '-' NUM { strcpy($$,"-"); strcat($$,$2); } ; pos_neg_num : pos_num { strcpy($$,$1); } | neg_num { strcpy($$,$1); } ; num_identifier : NUM {strcpy($$,$1);} | IDENTIFIER {strcpy($$,$1);} ; pos_neg_identifier : pos_neg_num {strcpy($$,$1);} | IDENTIFIER {strcpy($$,$1);} ; constant: '(' pos_neg_num ')' { $$ = NEW_NODE (TYPE_CONSTANT); set_str_value ($$, $2); } | IDENTIFIER'('pos_neg_num')' { $$ = NEW_NODE (TYPE_CONSTANT); set_name ($$, $1); set_str_value ($$, $3); } ; constant_list: constant { $$=$1; } | constant_list ',' constant { $$ = $1; append_right ($1, $3); } ; +/* identifier_list : IDENTIFIER { $$ = NEW_NODE (TYPE_IDENTIFIER); set_name($$,$1); } | identifier_list IDENTIFIER { AsnNode node; $$=$1; node = NEW_NODE (TYPE_IDENTIFIER); set_name (node, $2); append_right ($$, node); } ; +*/ obj_constant: num_identifier { $$ = NEW_NODE (TYPE_CONSTANT); set_str_value ($$, $1); } | IDENTIFIER '(' NUM ')' { $$ = NEW_NODE (TYPE_CONSTANT); set_name ($$, $1); set_str_value ($$, $3); } ; obj_constant_list: obj_constant { $$=$1;} | obj_constant_list obj_constant { $$=$1; append_right ($$, $2); } ; class : UNIVERSAL { $$ = CLASS_UNIVERSAL; } | PRIVATE { $$ = CLASS_PRIVATE; } | APPLICATION { $$ = CLASS_APPLICATION; } ; tag_type : '[' NUM ']' { $$ = NEW_NODE (TYPE_TAG); $$->flags.class = CLASS_CONTEXT; set_ulong_value ($$, $2); } | '[' class NUM ']' { $$ = NEW_NODE (TYPE_TAG); $$->flags.class = $2; set_ulong_value ($$, $3); } ; tag : tag_type { $$ = $1; } | tag_type EXPLICIT { $$ = $1; $$->flags.explicit = 1; } | tag_type IMPLICIT { $$ = $1; $$->flags.implicit = 1; } ; default : DEFAULT pos_neg_identifier { $$ = NEW_NODE (TYPE_DEFAULT); set_str_value ($$, $2); } | DEFAULT ksba_TRUE { $$ = NEW_NODE (TYPE_DEFAULT); $$->flags.is_true = 1; } | DEFAULT ksba_FALSE { $$ = NEW_NODE (TYPE_DEFAULT); $$->flags.is_false = 1; } ; integer_def: INTEGER { $$ = NEW_NODE (TYPE_INTEGER); } | INTEGER '{' constant_list '}' { $$ = NEW_NODE (TYPE_INTEGER); $$->flags.has_list = 1; set_down ($$, $3); } | integer_def '(' num_identifier '.' '.' num_identifier ')' { $$ = NEW_NODE (TYPE_INTEGER); $$->flags.has_min_max = 1; /* the following is wrong. Better use a union for the value*/ set_down ($$, NEW_NODE (TYPE_SIZE) ); set_str_value ($$->down, $6); set_name ($$->down, $3); } ; boolean_def: ksba_BOOLEAN { $$ = NEW_NODE (TYPE_BOOLEAN); } ; Time: UTCTime { $$ = NEW_NODE (TYPE_UTC_TIME); } | GeneralizedTime { $$ = NEW_NODE (TYPE_GENERALIZED_TIME); } ; size_def2: SIZE '(' num_identifier ')' { $$ = NEW_NODE (TYPE_SIZE); $$->flags.one_param = 1; set_str_value ($$, $3); } | SIZE '(' num_identifier '.' '.' num_identifier ')' { $$ = NEW_NODE (TYPE_SIZE); $$->flags.has_min_max = 1; set_str_value ($$, $3); set_name ($$, $6); } ; size_def: size_def2 { $$=$1; } | '(' size_def2 ')' { $$=$2; } ; octet_string_def : OCTET STRING { $$ = NEW_NODE (TYPE_OCTET_STRING); } | OCTET STRING size_def { $$ = NEW_NODE (TYPE_OCTET_STRING); $$->flags.has_size = 1; set_down ($$,$3); } ; utf8_string_def: UTF8STRING { $$ = NEW_NODE (TYPE_UTF8_STRING); } | UTF8STRING size_def { $$ = NEW_NODE (TYPE_UTF8_STRING); $$->flags.has_size = 1; set_down ($$,$2); } ; numeric_string_def: NUMERICSTRING { $$ = NEW_NODE (TYPE_NUMERIC_STRING); } | NUMERICSTRING size_def { $$ = NEW_NODE (TYPE_NUMERIC_STRING); $$->flags.has_size = 1; set_down ($$,$2); } ; printable_string_def: PRINTABLESTRING { $$ = NEW_NODE (TYPE_PRINTABLE_STRING); } | PRINTABLESTRING size_def { $$ = NEW_NODE (TYPE_PRINTABLE_STRING); $$->flags.has_size = 1; set_down ($$,$2); } ; teletex_string_def: TELETEXSTRING { $$ = NEW_NODE (TYPE_TELETEX_STRING); } | TELETEXSTRING size_def { $$ = NEW_NODE (TYPE_TELETEX_STRING); $$->flags.has_size = 1; set_down ($$,$2); } ; ia5_string_def: IA5STRING { $$ = NEW_NODE (TYPE_IA5_STRING); } | IA5STRING size_def { $$ = NEW_NODE (TYPE_IA5_STRING); $$->flags.has_size = 1; set_down ($$,$2); } ; universal_string_def: UNIVERSALSTRING { $$ = NEW_NODE (TYPE_UNIVERSAL_STRING); } | UNIVERSALSTRING size_def { $$ = NEW_NODE (TYPE_UNIVERSAL_STRING); $$->flags.has_size = 1; set_down ($$,$2); } ; bmp_string_def: BMPSTRING { $$ = NEW_NODE (TYPE_BMP_STRING); } | BMPSTRING size_def { $$ = NEW_NODE (TYPE_BMP_STRING); $$->flags.has_size = 1; set_down ($$,$2); } ; string_def: utf8_string_def | numeric_string_def | printable_string_def | teletex_string_def | ia5_string_def | universal_string_def | bmp_string_def ; bit_element : IDENTIFIER'('NUM')' { $$ = NEW_NODE (TYPE_CONSTANT); set_name ($$, $1); set_str_value ($$, $3); } ; bit_element_list : bit_element { $$=$1; } | bit_element_list ',' bit_element { $$=$1; append_right ($$, $3); } ; bit_string_def : BIT STRING { $$ = NEW_NODE (TYPE_BIT_STRING); } | BIT STRING '{' bit_element_list '}' { $$ = NEW_NODE (TYPE_BIT_STRING); $$->flags.has_list = 1; set_down ($$, $4); } ; enumerated_def : ENUMERATED '{' bit_element_list '}' { $$ = NEW_NODE (TYPE_ENUMERATED); $$->flags.has_list = 1; set_down ($$, $3); } ; object_def : OBJECT STR_IDENTIFIER { $$ = NEW_NODE (TYPE_OBJECT_ID); } ; type_assig_right: IDENTIFIER { $$ = NEW_NODE (TYPE_IDENTIFIER); set_str_value ($$, $1); } | IDENTIFIER size_def { $$ = NEW_NODE (TYPE_IDENTIFIER); $$->flags.has_size = 1; set_str_value ($$, $1); set_down ($$, $2); } | integer_def {$$=$1;} | enumerated_def {$$=$1;} | boolean_def {$$=$1;} | string_def {$$=$1;} | Time | octet_string_def {$$=$1;} | bit_string_def {$$=$1;} | sequence_def {$$=$1;} | object_def {$$=$1;} | choise_def {$$=$1;} | any_def {$$=$1;} | set_def {$$=$1;} | TOKEN_NULL { $$ = NEW_NODE(TYPE_NULL); } ; type_assig_right_tag : type_assig_right { $$ = $1; } | tag type_assig_right { /* $2->flags.has_tag = 1; */ /* $$ = $2; */ /* set_right ($1, $$->down ); */ /* set_down ($$, $1); */ $$ = $1; set_down ($$, $2); } ; type_assig_right_tag_default : type_assig_right_tag { $$ = $1; } | type_assig_right_tag default { $1->flags.has_default = 1; $$ = $1; set_right ($2, $$->down); set_down ($$, $2); } | type_assig_right_tag OPTIONAL { $1->flags.is_optional = 1; $$ = $1; } ; type_assig : IDENTIFIER type_assig_right_tag_default { set_name ($2, $1); $$ = $2; } ; type_assig_list : type_assig { $$=$1; } | type_assig_list ',' type_assig { $$=$1; append_right ($$, $3); } ; sequence_def : SEQUENCE '{' type_assig_list '}' { $$ = NEW_NODE (TYPE_SEQUENCE); set_down ($$, $3); } | SEQUENCE OF type_assig_right { $$ = NEW_NODE (TYPE_SEQUENCE_OF); set_down ($$, $3); } | SEQUENCE size_def OF type_assig_right { $$ = NEW_NODE (TYPE_SEQUENCE_OF); $$->flags.has_size = 1; set_right ($2,$4); set_down ($$,$2); } ; set_def : SET '{' type_assig_list '}' { $$ = NEW_NODE (TYPE_SET); set_down ($$, $3); } | SET OF type_assig_right { $$ = NEW_NODE (TYPE_SET_OF); set_down ($$, $3); } | SET size_def OF type_assig_right { $$ = NEW_NODE (TYPE_SET_OF); $$->flags.has_size = 1; set_right ($2, $4); set_down ($$, $2); } ; choise_def : CHOICE '{' type_assig_list '}' { $$ = NEW_NODE (TYPE_CHOICE); set_down ($$, $3); } ; any_def : ANY { $$ = NEW_NODE (TYPE_ANY); } | ANY DEFINED BY IDENTIFIER { AsnNode node; $$ = NEW_NODE (TYPE_ANY); $$->flags.has_defined_by = 1; node = NEW_NODE (TYPE_CONSTANT); set_name (node, $4); set_down($$, node); } ; type_def : IDENTIFIER "::=" type_assig_right_tag { set_name ($3, $1); $$ = $3; } ; constant_def : IDENTIFIER OBJECT STR_IDENTIFIER "::=" '{' obj_constant_list '}' { $$ = NEW_NODE (TYPE_OBJECT_ID); $$->flags.assignment = 1; set_name ($$, $1); set_down ($$, $6); } | IDENTIFIER IDENTIFIER "::=" '{' obj_constant_list '}' { $$ = NEW_NODE (TYPE_OBJECT_ID); $$->flags.assignment = 1; $$->flags.one_param = 1; set_name ($$, $1); set_str_value ($$, $2); set_down ($$, $5); } | IDENTIFIER INTEGER "::=" NUM { $$ = NEW_NODE (TYPE_INTEGER); $$->flags.assignment = 1; set_name ($$, $1); set_str_value ($$, $4); } ; type_constant: type_def { $$ = $1; } | constant_def { $$ = $1; } ; type_constant_list : type_constant { $$ = $1; } | type_constant_list type_constant { $$ = $1; append_right ($$, $2); } ; definitions_id : IDENTIFIER '{' obj_constant_list '}' { $$ = NEW_NODE (TYPE_OBJECT_ID); set_down ($$, $3); set_name ($$, $1); } ; imports_def : /* empty */ { $$=NULL;} - | IMPORTS identifier_list FROM IDENTIFIER obj_constant_list +/* + | IMPORTS identifier_list FROM IDENTIFIER { AsnNode node; $$ = NEW_NODE (TYPE_IMPORTS); node = NEW_NODE (TYPE_OBJECT_ID); set_name (node, $4); set_down (node, $5); set_down ($$, node); set_right ($$, $2); } +*/ ; explicit_implicit : EXPLICIT { $$ = CONST_EXPLICIT; } | IMPLICIT { $$ = CONST_IMPLICIT; } ; definitions: definitions_id DEFINITIONS explicit_implicit TAGS "::=" ksba_BEGIN imports_def type_constant_list ksba_END { AsnNode node; $$ = node = NEW_NODE (TYPE_DEFINITIONS); if ($3 == CONST_EXPLICIT) node->flags.explicit = 1; else if ($3 == CONST_IMPLICIT) node->flags.implicit = 1; if ($7) node->flags.has_imports = 1; set_name ($$, $1->name); set_name ($1, ""); if (!node->flags.has_imports) set_right ($1,$8); else { set_right ($7,$8); set_right ($1,$7); } set_down ($$, $1); _ksba_asn_set_default_tag ($$); _ksba_asn_type_set_config ($$); PARSECTL->result_parse = _ksba_asn_check_identifier($$); PARSECTL->parse_tree=$$; } ; %% /*************************************************************/ /* Function: yylex */ /* Description: looks for tokens in file_asn1 pointer file. */ /* Return: int */ /* Token identifier or ASCII code or 0(zero: End Of File) */ /*************************************************************/ static int yylex (YYSTYPE *lvalp, void *parm) { int c,counter=0,k; char string[MAX_STRING_LENGTH]; size_t len; FILE *fp = PARSECTL->fp; if (!PARSECTL->lineno) PARSECTL->lineno++; /* start with line one */ while (1) { while ( (c=fgetc (fp))==' ' || c=='\t') ; if (c =='\n') { PARSECTL->lineno++; continue; } if(c==EOF) return 0; if ( c=='(' || c==')' || c=='[' || c==']' || c=='{' || c=='}' || c==',' || c=='.' || c=='+') return c; if (c=='-') { if ( (c=fgetc(fp))!='-') { ungetc(c,fp); return '-'; } else { /* A comment finishes at the end of line */ counter=0; while ( (c=fgetc(fp))!=EOF && c != '\n' ) ; if (c==EOF) return 0; else continue; /* repeat the search */ } } do { if (counter >= DIM (string)-1 ) { fprintf (stderr,"%s:%d: token too long\n", "myfile:", PARSECTL->lineno); return 0; /* EOF */ } string[counter++]=c; } while ( !((c=fgetc(fp))==EOF || c==' '|| c=='\t' || c=='\n' || c=='(' || c==')' || c=='[' || c==']' || c=='{' || c=='}' || c==',' || c=='.')); ungetc (c,fp); string[counter]=0; /*fprintf (stderr, "yylex token `%s'\n", string);*/ /* Is STRING a number? */ for (k=0; k=counter) { strcpy (lvalp->str,string); if (PARSECTL->debug) fprintf (stderr,"%d: yylex found number `%s'\n", PARSECTL->lineno, string); return NUM; } /* Is STRING a keyword? */ len = strlen (string); for (k = 0; k < YYNTOKENS; k++) { if (yytname[k] && yytname[k][0] == '\"' && !strncmp (yytname[k] + 1, string, len) && yytname[k][len + 1] == '\"' && !yytname[k][len + 2]) return yytoknum[k]; } /* STRING is an IDENTIFIER */ strcpy(lvalp->str,string); if (PARSECTL->debug) fprintf (stderr,"%d: yylex found identifier `%s'\n", PARSECTL->lineno, string); return IDENTIFIER; } } static void yyerror (void *parm, const char *s) { (void)parm; /* Sends the error description to stderr */ fprintf (stderr, "%s\n", s); /* Why doesn't bison provide a way to pass the parm to yyerror? Update: Newer bison versions allow for this. We need to see how we can make use of it. */ } static AsnNode new_node (struct parser_control_s *parsectl, node_type_t type) { AsnNode node; node = xcalloc (1, sizeof *node); node->type = type; node->off = -1; node->link_next = parsectl->all_nodes; parsectl->all_nodes = node; return node; } static void release_all_nodes (AsnNode node) { AsnNode node2; for (; node; node = node2) { node2 = node->link_next; xfree (node->name); if (node->valuetype == VALTYPE_CSTR) xfree (node->value.v_cstr); else if (node->valuetype == VALTYPE_MEM) xfree (node->value.v_mem.buf); xfree (node); } } static void set_name (AsnNode node, const char *name) { _ksba_asn_set_name (node, name); } static void set_str_value (AsnNode node, const char *text) { if (text && *text) _ksba_asn_set_value (node, VALTYPE_CSTR, text, 0); else _ksba_asn_set_value (node, VALTYPE_NULL, NULL, 0); } static void set_ulong_value (AsnNode node, const char *text) { unsigned long val; if (text && *text) val = strtoul (text, NULL, 10); else val = 0; _ksba_asn_set_value (node, VALTYPE_ULONG, &val, sizeof(val)); } static void set_right (AsnNode node, AsnNode right) { return_if_fail (node); node->right = right; if (right) right->left = node; } static void append_right (AsnNode node, AsnNode right) { return_if_fail (node); while (node->right) node = node->right; node->right = right; if (right) right->left = node; } static void set_down (AsnNode node, AsnNode down) { return_if_fail (node); node->down = down; if (down) down->left = node; } /** * ksba_asn_parse_file: * @file_name: Filename with the ASN module * @pointer: Returns the syntax tree * @debug: Enable debug output * * Parse an ASN.1 file and return an syntax tree. * * Return value: 0 for okay or an ASN_xx error code **/ int ksba_asn_parse_file (const char *file_name, ksba_asn_tree_t *result, int debug) { struct parser_control_s parsectl; *result = NULL; parsectl.fp = file_name? fopen (file_name, "r") : NULL; if ( !parsectl.fp ) return gpg_error_from_syserror (); parsectl.lineno = 0; parsectl.debug = debug; parsectl.result_parse = gpg_error (GPG_ERR_SYNTAX); parsectl.parse_tree = NULL; parsectl.all_nodes = NULL; /* yydebug = 1; */ if ( yyparse ((void*)&parsectl) || parsectl.result_parse ) { /* error */ fprintf (stderr, "%s:%d: parse error\n", file_name?file_name:"-", parsectl.lineno ); release_all_nodes (parsectl.all_nodes); parsectl.all_nodes = NULL; } else { /* okay */ ksba_asn_tree_t tree; _ksba_asn_change_integer_value (parsectl.parse_tree); _ksba_asn_expand_object_id (parsectl.parse_tree); tree = xmalloc ( sizeof *tree + (file_name? strlen (file_name):1) ); tree->parse_tree = parsectl.parse_tree; tree->node_list = parsectl.all_nodes; strcpy (tree->filename, file_name? file_name:"-"); *result = tree; } if (file_name) fclose (parsectl.fp); return parsectl.result_parse; } void ksba_asn_tree_release (ksba_asn_tree_t tree) { if (!tree) return; release_all_nodes (tree->node_list); tree->node_list = NULL; xfree (tree); } void _ksba_asn_release_nodes (AsnNode node) { /* FIXME: it does not work yet because the allocation function in asn1-func.c does not link all nodes together */ release_all_nodes (node); } diff --git a/src/cms-parser.c b/src/cms-parser.c index e4da08c..9c0f836 100644 --- a/src/cms-parser.c +++ b/src/cms-parser.c @@ -1,968 +1,968 @@ /* cms-parse.c - parse cryptographic message syntax * Copyright (C) 2001, 2012 g10 Code GmbH * * This file is part of KSBA. * * KSBA is free software; you can redistribute it and/or modify * it under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * KSBA 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 copies of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, see . */ /* We handle CMS by using a handcrafted parser for the outer structures and the generic parser of the parts we can handle in memory. Extending the generic parser to allow hooks for indefinite length objects and to auto select the object depending on the content type OID is too complicated. */ #include #include #include #include #include #include "util.h" #include "cms.h" #include "asn1-func.h" /* need some constants */ #include "ber-decoder.h" #include "ber-help.h" #include "keyinfo.h" static int read_byte (ksba_reader_t reader) { unsigned char buf; size_t nread; int rc; do rc = ksba_reader_read (reader, &buf, 1, &nread); while (!rc && !nread); return rc? -1: buf; } /* read COUNT bytes into buffer. Return 0 on success */ static int read_buffer (ksba_reader_t reader, char *buffer, size_t count) { size_t nread; while (count) { if (ksba_reader_read (reader, buffer, count, &nread)) return -1; buffer += nread; count -= nread; } return 0; } /* Create a new decoder and run it for the given element */ static gpg_error_t create_and_run_decoder (ksba_reader_t reader, const char *elem_name, unsigned int flags, AsnNode *r_root, unsigned char **r_image, size_t *r_imagelen) { gpg_error_t err; ksba_asn_tree_t cms_tree; BerDecoder decoder; err = ksba_asn_create_tree ("cms", &cms_tree); if (err) return err; decoder = _ksba_ber_decoder_new (); if (!decoder) { ksba_asn_tree_release (cms_tree); return gpg_error (GPG_ERR_ENOMEM); } err = _ksba_ber_decoder_set_reader (decoder, reader); if (err) { ksba_asn_tree_release (cms_tree); _ksba_ber_decoder_release (decoder); return err; } err = _ksba_ber_decoder_set_module (decoder, cms_tree); if (err) { ksba_asn_tree_release (cms_tree); _ksba_ber_decoder_release (decoder); return err; } err = _ksba_ber_decoder_decode (decoder, elem_name, flags, r_root, r_image, r_imagelen); _ksba_ber_decoder_release (decoder); ksba_asn_tree_release (cms_tree); return err; } /* Parse this structure and return the oid of the content. The read position is then located at the value of content. This fucntion is the core for parsing ContentInfo and EncapsulatedContentInfo. ContentInfo ::= SEQUENCE { contentType ContentType, content [0] EXPLICIT ANY DEFINED BY contentType } ContentType ::= OBJECT IDENTIFIER Returns: 0 on success or an error code. Other values are returned by the parameters. */ static gpg_error_t parse_content_info (ksba_reader_t reader, unsigned long *r_len, int *r_ndef, char **r_oid, int *has_content) { struct tag_info ti; gpg_error_t err; int content_ndef; unsigned long content_len; unsigned char oidbuf[100]; /* pretty large for an OID */ char *oid = NULL; /* read the sequence triplet */ err = _ksba_ber_read_tl (reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CMS_OBJ); content_len = ti.length; content_ndef = ti.ndef; if (!content_ndef && content_len < 3) return gpg_error (GPG_ERR_TOO_SHORT); /* to encode an OID */ /* read the OID */ err = _ksba_ber_read_tl (reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID && !ti.is_constructed && ti.length) ) return gpg_error (GPG_ERR_INV_CMS_OBJ); if (!content_ndef) { if (content_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */ content_len -= ti.nhdr; if (content_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */ content_len -= ti.length; } if (ti.length >= DIM(oidbuf)) return gpg_error (GPG_ERR_TOO_LARGE); err = read_buffer (reader, oidbuf, ti.length); if (err) return err; oid = ksba_oid_to_str (oidbuf, ti.length); if (!oid) return gpg_error (GPG_ERR_ENOMEM); if (!content_ndef && !content_len) { /* no data */ *has_content = 0; } else { /* now read the explicit tag 0 which is optional */ err = _ksba_ber_read_tl (reader, &ti); if (err) { xfree (oid); return err; } if ( ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed ) { *has_content = 1; } else if ( ti.class == CLASS_UNIVERSAL && ti.tag == 0 && !ti.is_constructed ) { *has_content = 0; /* this is optional - allow NUL tag */ } else /* neither [0] nor NULL */ { xfree (oid); return gpg_error (GPG_ERR_INV_CMS_OBJ); } if (!content_ndef) { if (content_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */ content_len -= ti.nhdr; if (!ti.ndef && content_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */ } } *r_len = content_len; *r_ndef = content_ndef; *r_oid = oid; return 0; } /* Parse this structure and return the oid of the content as well as the algorithm identifier. The read position is then located at the value of the octect string. EncryptedContentInfo ::= SEQUENCE { contentType OBJECT IDENTIFIER, contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, encryptedContent [0] IMPLICIT OCTET STRING OPTIONAL } Returns: 0 on success or an error code. Other values are returned by the parameters. */ static gpg_error_t parse_encrypted_content_info (ksba_reader_t reader, unsigned long *r_len, int *r_ndef, char **r_cont_oid, char **r_algo_oid, char **r_algo_parm, size_t *r_algo_parmlen, int *has_content) { struct tag_info ti; gpg_error_t err; int content_ndef; unsigned long content_len; unsigned char tmpbuf[500]; /* for OID or algorithmIdentifier */ char *cont_oid = NULL; char *algo_oid = NULL; char *algo_parm = NULL; size_t algo_parmlen; size_t nread; /* Fixme: release oids in case of errors */ /* read the sequence triplet */ err = _ksba_ber_read_tl (reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CMS_OBJ); content_len = ti.length; content_ndef = ti.ndef; if (!content_ndef && content_len < 3) return gpg_error (GPG_ERR_TOO_SHORT); /* to encode an OID */ /* read the OID */ err = _ksba_ber_read_tl (reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID && !ti.is_constructed && ti.length) ) return gpg_error (GPG_ERR_INV_CMS_OBJ); if (!content_ndef) { if (content_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */ content_len -= ti.nhdr; if (content_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */ content_len -= ti.length; } if (ti.length >= DIM(tmpbuf)) return gpg_error (GPG_ERR_TOO_LARGE); err = read_buffer (reader, tmpbuf, ti.length); if (err) return err; cont_oid = ksba_oid_to_str (tmpbuf, ti.length); if (!cont_oid) return gpg_error (GPG_ERR_ENOMEM); /* read the algorithmIdentifier */ err = _ksba_ber_read_tl (reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CMS_OBJ); if (!content_ndef) { if (content_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */ content_len -= ti.nhdr; if (content_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */ content_len -= ti.length; } if (ti.nhdr + ti.length >= DIM(tmpbuf)) return gpg_error (GPG_ERR_TOO_LARGE); memcpy (tmpbuf, ti.buf, ti.nhdr); err = read_buffer (reader, tmpbuf+ti.nhdr, ti.length); if (err) return err; err = _ksba_parse_algorithm_identifier2 (tmpbuf, ti.nhdr+ti.length, &nread,&algo_oid, &algo_parm, &algo_parmlen); if (err) return err; assert (nread <= ti.nhdr + ti.length); if (nread < ti.nhdr + ti.length) return gpg_error (GPG_ERR_TOO_SHORT); /* the optional encryptedDataInfo */ *has_content = 0; if (content_ndef || content_len) { /* now read the implicit tag 0. Actually this is optional but in that case we don't expect to have a content_len - well, it may be the end tag */ err = _ksba_ber_read_tl (reader, &ti); if (err) { xfree (cont_oid); xfree (algo_oid); return err; } /* Note: the tag may either denote a constructed or a primitve object. Actually this should match the use of NDEF header but we don't ceck that */ if ( ti.class == CLASS_CONTEXT && ti.tag == 0 ) { *has_content = 1; if (!content_ndef) { if (content_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); content_len -= ti.nhdr; if (!ti.ndef && content_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); } } else /* not what we want - push it back */ { *has_content = 0; err = ksba_reader_unread (reader, ti.buf, ti.nhdr); if (err) return err; } } *r_len = content_len; *r_ndef = content_ndef; *r_cont_oid = cont_oid; *r_algo_oid = algo_oid; *r_algo_parm = algo_parm; *r_algo_parmlen = algo_parmlen; return 0; } /* Parse this structure and return the oid of the content. The read position is then located at the value of content. ContentInfo ::= SEQUENCE { contentType ContentType, content [0] EXPLICIT ANY DEFINED BY contentType } ContentType ::= OBJECT IDENTIFIER Returns: 0 on success or an error code. On success the OID and the length values are stored in the cms structure. */ gpg_error_t _ksba_cms_parse_content_info (ksba_cms_t cms) { gpg_error_t err; int has_content; int content_ndef; unsigned long content_len; char *oid; err = parse_content_info (cms->reader, &content_len, &content_ndef, &oid, &has_content); if (err) { /* return a more meaningful error message. This way the caller can pass arbitrary data to the function and get back an error that this is not CMS instead of the the not very detailed BER Error. */ if (gpg_err_code (err) == GPG_ERR_BAD_BER || gpg_err_code (err) == GPG_ERR_INV_CMS_OBJ || gpg_err_code (err) == GPG_ERR_TOO_SHORT) err = gpg_error (GPG_ERR_NO_CMS_OBJ); return err; } if (!has_content) return gpg_error (GPG_ERR_NO_CMS_OBJ); /* It is not optional here */ cms->content.length = content_len; cms->content.ndef = content_ndef; xfree (cms->content.oid); cms->content.oid = oid; return 0; } /* parse a SEQUENCE and the first element which is expected to be the CMS version. Return the version and the length info */ static gpg_error_t parse_cms_version (ksba_reader_t reader, int *r_version, unsigned long *r_len, int *r_ndef) { struct tag_info ti; gpg_error_t err; unsigned long data_len; int data_ndef; int c; /* read the sequence triplet */ err = _ksba_ber_read_tl (reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CMS_OBJ); data_len = ti.length; data_ndef = ti.ndef; if (!data_ndef && data_len < 3) return gpg_error (GPG_ERR_TOO_SHORT); /*to encode the version*/ /* read the version integer */ err = _ksba_ber_read_tl (reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_INTEGER && !ti.is_constructed && ti.length) ) return gpg_error (GPG_ERR_INV_CMS_OBJ); if (!data_ndef) { if (data_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */ data_len -= ti.nhdr; if (data_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */ data_len -= ti.length; } if (ti.length != 1) return gpg_error (GPG_ERR_UNSUPPORTED_CMS_VERSION); if ( (c=read_byte (reader)) == -1) { err = ksba_reader_error (reader); return err? err : gpg_error (GPG_ERR_GENERAL); } if ( !(c == 0 || c == 1 || c == 2 || c == 3 || c == 4) ) return gpg_error (GPG_ERR_UNSUPPORTED_CMS_VERSION); *r_version = c; *r_len = data_len; *r_ndef = data_ndef; return 0; } /* Parse a structure: SignedData ::= SEQUENCE { version INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4) }), digestAlgorithms SET OF AlgorithmIdentifier, encapContentInfo EncapsulatedContentInfo, certificates [0] IMPLICIT CertificateSet OPTIONAL, crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, signerInfos SignerInfos } AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL } */ gpg_error_t _ksba_cms_parse_signed_data_part_1 (ksba_cms_t cms) { struct tag_info ti; gpg_error_t err; int signed_data_ndef; unsigned long signed_data_len; int algo_set_ndef; unsigned long algo_set_len; int encap_cont_ndef; unsigned long encap_cont_len; int has_content; char *oid; char *p, *buffer; unsigned long off, len; err = parse_cms_version (cms->reader, &cms->cms_version, &signed_data_len, &signed_data_ndef); if (err) return err; /* read the SET OF algorithmIdentifiers */ err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SET && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CMS_OBJ); /* not the expected SET tag */ if (!signed_data_ndef) { if (signed_data_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */ signed_data_len -= ti.nhdr; if (!ti.ndef && signed_data_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */ signed_data_len -= ti.length; } algo_set_len = ti.length; algo_set_ndef = ti.ndef; /* fixme: we are not able to read ndef length algorithm indentifiers. */ if (algo_set_ndef) return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); /* read the entire sequence into a buffer (add one to avoid malloc(0)) */ buffer = xtrymalloc (algo_set_len + 1); if (!buffer) return gpg_error (GPG_ERR_ENOMEM); if (read_buffer (cms->reader, buffer, algo_set_len)) { xfree (buffer); err = ksba_reader_error (cms->reader); return err? err: gpg_error (GPG_ERR_GENERAL); } p = buffer; while (algo_set_len) { size_t nread; struct oidlist_s *ol; err = _ksba_parse_algorithm_identifier (p, algo_set_len, &nread, &oid); if (err) { xfree (buffer); return err; } assert (nread <= algo_set_len); algo_set_len -= nread; p += nread; /* store the oid */ ol = xtrymalloc (sizeof *ol); if (!ol) { xfree (oid); return gpg_error (GPG_ERR_ENOMEM); } ol->oid = oid; ol->next = cms->digest_algos; cms->digest_algos = ol; } xfree (buffer); buffer = NULL; /* Now for the encapsulatedContentInfo */ off = ksba_reader_tell (cms->reader); err = parse_content_info (cms->reader, &encap_cont_len, &encap_cont_ndef, &oid, &has_content); if (err) return err; cms->inner_cont_len = encap_cont_len; cms->inner_cont_ndef = encap_cont_ndef; cms->inner_cont_oid = oid; cms->detached_data = !has_content; if (!signed_data_ndef) { len = ksba_reader_tell (cms->reader) - off; if (signed_data_len < len) return gpg_error (GPG_ERR_BAD_BER); /* parsed content info larger that sequence */ signed_data_len -= len; if (!encap_cont_ndef && signed_data_len < encap_cont_len) return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */ } /* We have to stop here so that the caller can set up the hashing etc. */ return 0; } /* Continue parsing of the structure we started to parse with the part_1 function. We expect to be right at the certificates tag. */ gpg_error_t _ksba_cms_parse_signed_data_part_2 (ksba_cms_t cms) { struct tag_info ti; gpg_error_t err; struct signer_info_s *si, **si_tail; /* read the next triplet which is either a [0], a [1] or a SET OF (signerInfo) */ err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed) { /* well, there might be still an end tag pending; eat it - fixme: we should keep track of this to catch invalid encodings */ err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; } if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed) { /* Implicit SET OF certificateSet with elements of CHOICE, but we assume the first choice which is a Certificate; all other choices are obsolete. We are now parsing a set of certificates which we do by utilizing the ksba_cert code. */ ksba_cert_t cert; int expect_endtag; expect_endtag = !!ti.ndef; for (;;) { struct certlist_s *cl; /* First see whether this is really a sequence */ err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; if (expect_endtag && !ti.class && ti.tag == TYPE_NULL ) { /* This is an end tag. Read the next tag but don't fail if this is just an EOF. */ err = _ksba_ber_read_tl (cms->reader, &ti); if (err) { if (gpg_err_code (err) == GPG_ERR_EOF) err = 0; return err; } break; } if (!(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed)) break; /* not a sequence, so we are ready with the set */ /* We must unread so that the standard parser sees the sequence */ err = ksba_reader_unread (cms->reader, ti.buf, ti.nhdr); if (err) return err; /* Use the standard certificate parser */ err = ksba_cert_new (&cert); if (err) return err; err = ksba_cert_read_der (cert, cms->reader); if (err) { ksba_cert_release (cert); return err; } cl = xtrycalloc (1, sizeof *cl); if (!cl) { ksba_cert_release (cert); return gpg_error (GPG_ERR_ENOMEM); } cl->cert = cert; cl->next = cms->cert_list; cms->cert_list = cl; } } if (ti.class == CLASS_CONTEXT && ti.tag == 1 && ti.is_constructed) { /* implicit SET OF certificateList. We should delegate the parsing to a - not yet existing - ksba_crl module. CRLs are quite important for other applications too so we should provide a nice interface */ int expect_endtag; expect_endtag = !!ti.ndef; /* FIXME this is just dummy read code */ /* fprintf (stderr,"WARNING: Can't handle CRLs yet\n"); */ for (;;) { /* first see whether this is really a sequence */ err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; if (expect_endtag && !ti.class && ti.tag == TYPE_NULL ) { /* This is an end tag. Read the next tag but don't fail if this is just an EOF. */ err = _ksba_ber_read_tl (cms->reader, &ti); if (err) { if (gpg_err_code (err) == GPG_ERR_EOF) err = 0; return err; } break; } if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed)) break; /* not a sequence, so we are ready with the set */ while (ti.length) { size_t n, nread; char dummy[256]; n = ti.length > DIM(dummy) ? DIM(dummy): ti.length; err = ksba_reader_read (cms->reader, dummy, n, &nread); if (err) return err; ti.length -= nread; } } } /* expect a SET OF signerInfo */ if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SET && ti.is_constructed)) return gpg_error (GPG_ERR_INV_CMS_OBJ); si_tail = &cms->signer_info; while (ti.length) { size_t off1, off2; off1 = ksba_reader_tell (cms->reader); si = xtrycalloc (1, sizeof *si); if (!si) return gpg_error (GPG_ERR_ENOMEM); err = create_and_run_decoder (cms->reader, "CryptographicMessageSyntax.SignerInfo", 0, &si->root, &si->image, &si->imagelen); /* The signerInfo might be an empty set in the case of a certs-only signature. Thus we have to allow for EOF here */ if (gpg_err_code (err) == GPG_ERR_EOF) { xfree (si); err = 0; break; } if (err) { xfree (si); return err; } *si_tail = si; si_tail = &si->next; off2 = ksba_reader_tell (cms->reader); if ( (off2 - off1) > ti.length ) ti.length = 0; else ti.length -= off2 - off1; } return 0; } /* Parse the structure: EnvelopedData ::= SEQUENCE { version INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4) }), originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL, recipientInfos RecipientInfos, encryptedContentInfo EncryptedContentInfo, unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL } OriginatorInfo ::= SEQUENCE { certs [0] IMPLICIT CertificateSet OPTIONAL, crls [1] IMPLICIT CertificateRevocationLists OPTIONAL } RecipientInfos ::= SET OF RecipientInfo EncryptedContentInfo ::= SEQUENCE { contentType ContentType, contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL } EncryptedContent ::= OCTET STRING We stop parsing so that the next read will be the first byte of the encryptedContent or (if there is no content) the unprotectedAttrs. */ gpg_error_t _ksba_cms_parse_enveloped_data_part_1 (ksba_cms_t cms) { struct tag_info ti; gpg_error_t err; int env_data_ndef; unsigned long env_data_len; - int encr_cont_ndef; - unsigned long encr_cont_len; - int has_content; + int encr_cont_ndef = 0; + unsigned long encr_cont_len = 0; + int has_content = 0; unsigned long off, len; char *cont_oid = NULL; char *algo_oid = NULL; char *algo_parm = NULL; - size_t algo_parmlen; + size_t algo_parmlen = 0; struct value_tree_s *vt, **vtend; /* get the version */ err = parse_cms_version (cms->reader, &cms->cms_version, &env_data_len, &env_data_ndef); if (err) return err; /* read the next triplet which is either a [0] for originatorInfos or a SET_OF (recipientInfo) */ err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed) { /* originatorInfo - but we skip it for now */ /* well, raise an error */ return gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ); } /* Next one is the SET OF recipientInfos */ if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SET && ti.is_constructed)) return gpg_error (GPG_ERR_INV_CMS_OBJ); vtend = &cms->recp_info; if (ti.ndef) { for (;;) { struct tag_info ti2; err = _ksba_ber_read_tl (cms->reader, &ti2); if (err) return err; if (!ti2.class && !ti2.tag) break; /* End tag found: ready. */ /* Not an end tag: Push it back and run the decoder. */ err = ksba_reader_unread (cms->reader, ti2.buf, ti2.nhdr); if (err) return err; vt = xtrycalloc (1, sizeof *vt); if (!vt) return gpg_error_from_syserror (); err = create_and_run_decoder (cms->reader, "CryptographicMessageSyntax.KeyTransRecipientInfo", BER_DECODER_FLAG_FAST_STOP, &vt->root, &vt->image, &vt->imagelen); if (err) { xfree (vt); return err; } *vtend = vt; vtend = &vt->next; } } else { while (ti.length) { size_t off1, off2; off1 = ksba_reader_tell (cms->reader); vt = xtrycalloc (1, sizeof *vt); if (!vt) return gpg_error_from_syserror (); err = create_and_run_decoder (cms->reader, "CryptographicMessageSyntax.KeyTransRecipientInfo", 0, &vt->root, &vt->image, &vt->imagelen); if (err) { xfree (vt); return err; } *vtend = vt; vtend = &vt->next; off2 = ksba_reader_tell (cms->reader); if ( (off2 - off1) > ti.length ) ti.length = 0; else ti.length -= off2 - off1; } } /* Now for the encryptedContentInfo */ off = ksba_reader_tell (cms->reader); err = parse_encrypted_content_info (cms->reader, &encr_cont_len, &encr_cont_ndef, &cont_oid, &algo_oid, &algo_parm, &algo_parmlen, &has_content); if (err) return err; cms->inner_cont_len = encr_cont_len; cms->inner_cont_ndef = encr_cont_ndef; cms->inner_cont_oid = cont_oid; cms->detached_data = !has_content; cms->encr_algo_oid = algo_oid; cms->encr_iv = algo_parm; algo_parm = NULL; cms->encr_ivlen = algo_parmlen; if (!env_data_ndef) { len = ksba_reader_tell (cms->reader) - off; if (env_data_len < len) return gpg_error (GPG_ERR_BAD_BER); /* parsed content info larger that sequence */ env_data_len -= len; if (!encr_cont_ndef && env_data_len < encr_cont_len) return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */ } return 0; } /* handle the unprotected attributes */ gpg_error_t _ksba_cms_parse_enveloped_data_part_2 (ksba_cms_t cms) { (void)cms; /* FIXME */ return 0; } diff --git a/src/crl.c b/src/crl.c index 87a3fa3..daeb222 100644 --- a/src/crl.c +++ b/src/crl.c @@ -1,1544 +1,1546 @@ /* crl.c - CRL parser * Copyright (C) 2002, 2004, 2005, 2012 g10 Code GmbH * * This file is part of KSBA. * * KSBA is free software; you can redistribute it and/or modify * it under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * KSBA 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 copies of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, see . */ #include #include #include #include #include #include #include "util.h" #include "convert.h" #include "keyinfo.h" #include "der-encoder.h" #include "ber-help.h" #include "ber-decoder.h" #include "crl.h" static const char oidstr_crlNumber[] = "2.5.29.20"; static const char oidstr_crlReason[] = "2.5.29.21"; +#if 0 static const char oidstr_issuingDistributionPoint[] = "2.5.29.28"; +#endif static const char oidstr_certificateIssuer[] = "2.5.29.29"; static const char oidstr_authorityKeyIdentifier[] = "2.5.29.35"; /* We better buffer the hashing. */ static inline void do_hash (ksba_crl_t crl, const void *buffer, size_t length) { while (length) { size_t n = length; if (crl->hashbuf.used + n > sizeof crl->hashbuf.buffer) n = sizeof crl->hashbuf.buffer - crl->hashbuf.used; memcpy (crl->hashbuf.buffer+crl->hashbuf.used, buffer, n); crl->hashbuf.used += n; if (crl->hashbuf.used == sizeof crl->hashbuf.buffer) { if (crl->hash_fnc) crl->hash_fnc (crl->hash_fnc_arg, crl->hashbuf.buffer, crl->hashbuf.used); crl->hashbuf.used = 0; } buffer = (const char *)buffer + n; length -= n; } } #define HASH(a,b) do_hash (crl, (a), (b)) static void parse_skip (unsigned char const **buf, size_t *len, struct tag_info *ti) { if (ti->length) { assert (ti->length <= *len); *len -= ti->length; *buf += ti->length; } } static gpg_error_t parse_sequence (unsigned char const **buf, size_t *len, struct tag_info *ti) { gpg_error_t err; err = _ksba_ber_parse_tl (buf, len, ti); if (err) ; else if (!(ti->class == CLASS_UNIVERSAL && ti->tag == TYPE_SEQUENCE && ti->is_constructed) ) err = gpg_error (GPG_ERR_INV_OBJ); else if (ti->length > *len) err = gpg_error (GPG_ERR_BAD_BER); return err; } static gpg_error_t parse_integer (unsigned char const **buf, size_t *len, struct tag_info *ti) { gpg_error_t err; err = _ksba_ber_parse_tl (buf, len, ti); if (err) ; else if (!(ti->class == CLASS_UNIVERSAL && ti->tag == TYPE_INTEGER && !ti->is_constructed) ) err = gpg_error (GPG_ERR_INV_OBJ); else if (!ti->length) err = gpg_error (GPG_ERR_TOO_SHORT); else if (ti->length > *len) err = gpg_error (GPG_ERR_BAD_BER); return err; } static gpg_error_t parse_octet_string (unsigned char const **buf, size_t *len, struct tag_info *ti) { gpg_error_t err; err= _ksba_ber_parse_tl (buf, len, ti); if (err) ; else if (!(ti->class == CLASS_UNIVERSAL && ti->tag == TYPE_OCTET_STRING && !ti->is_constructed) ) err = gpg_error (GPG_ERR_INV_OBJ); else if (!ti->length) err = gpg_error (GPG_ERR_TOO_SHORT); else if (ti->length > *len) err = gpg_error (GPG_ERR_BAD_BER); return err; } static gpg_error_t parse_object_id_into_str (unsigned char const **buf, size_t *len, char **oid) { struct tag_info ti; gpg_error_t err; *oid = NULL; err = _ksba_ber_parse_tl (buf, len, &ti); if (err) ; else if (!(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID && !ti.is_constructed) ) err = gpg_error (GPG_ERR_INV_OBJ); else if (!ti.length) err = gpg_error (GPG_ERR_TOO_SHORT); else if (ti.length > *len) err = gpg_error (GPG_ERR_BAD_BER); else if (!(*oid = ksba_oid_to_str (*buf, ti.length))) err = gpg_error_from_errno (errno); else { *buf += ti.length; *len -= ti.length; } return err; } /** * ksba_crl_new: * * Create a new and empty CRL object * * Return value: A CRL object or an error code. **/ gpg_error_t ksba_crl_new (ksba_crl_t *r_crl) { *r_crl = xtrycalloc (1, sizeof **r_crl); if (!*r_crl) return gpg_error_from_errno (errno); return 0; } /** * ksba_crl_release: * @crl: A CRL object * * Release a CRL object. **/ void ksba_crl_release (ksba_crl_t crl) { if (!crl) return; xfree (crl->algo.oid); xfree (crl->algo.parm); _ksba_asn_release_nodes (crl->issuer.root); xfree (crl->issuer.image); xfree (crl->item.serial); xfree (crl->sigval); while (crl->extension_list) { crl_extn_t tmp = crl->extension_list->next; xfree (crl->extension_list->oid); xfree (crl->extension_list); crl->extension_list = tmp; } xfree (crl); } gpg_error_t ksba_crl_set_reader (ksba_crl_t crl, ksba_reader_t r) { if (!crl || !r) return gpg_error (GPG_ERR_INV_VALUE); crl->reader = r; return 0; } /* Provide a hash function so that we are able to hash the data */ void ksba_crl_set_hash_function (ksba_crl_t crl, void (*hash_fnc)(void *, const void *, size_t), void *hash_fnc_arg) { if (crl) { crl->hash_fnc = hash_fnc; crl->hash_fnc_arg = hash_fnc_arg; } } /* access functions */ /** * ksba_crl_get_digest_algo: * @cms: CMS object * * Figure out the the digest algorithm used for the signature and return * its OID. * * Return value: NULL if the signature algorithm is not yet available * or there is a mismatched between "tbsCertList.signature" and * "signatureAlgorithm"; on success the OID is returned which is valid * as long as the CRL object is valid. **/ const char * ksba_crl_get_digest_algo (ksba_crl_t crl) { if (!crl) return NULL; /* fixme: implement the described check */ return crl->algo.oid; } /** * ksba_crl_get_issuer: * @cms: CMS object * @r_issuer: returns the issuer * * This functions returns the issuer of the CRL. The caller must * release the returned object. * * Return value: 0 on success or an error code **/ gpg_error_t ksba_crl_get_issuer (ksba_crl_t crl, char **r_issuer) { gpg_error_t err; AsnNode n; const unsigned char *image; if (!crl || !r_issuer) return gpg_error (GPG_ERR_INV_VALUE); if (!crl->issuer.root) return gpg_error (GPG_ERR_NO_DATA); n = crl->issuer.root; image = crl->issuer.image; if (!n || !n->down) return gpg_error (GPG_ERR_NO_VALUE); n = n->down; /* dereference the choice node */ if (n->off == -1) { /* fputs ("get_issuer problem at node:\n", stderr); */ /* _ksba_asn_node_dump_all (n, stderr); */ return gpg_error (GPG_ERR_GENERAL); } err = _ksba_dn_to_str (image, n, r_issuer); return err; } /* Return the CRL extension in OID, CRITICAL, DER and DERLEN. The caller should iterate IDX from 0 upwards until GPG_ERR_EOF is returned. Note, that the returned values are valid as long as the context is valid and no new parsing has been started. */ gpg_error_t ksba_crl_get_extension (ksba_crl_t crl, int idx, char const **oid, int *critical, unsigned char const **der, size_t *derlen) { crl_extn_t e; if (!crl) return gpg_error (GPG_ERR_INV_VALUE); if (idx < 0) return gpg_error (GPG_ERR_INV_INDEX); for (e=crl->extension_list; e && idx; e = e->next, idx-- ) ; if (!e) return gpg_error (GPG_ERR_EOF); if (oid) *oid = e->oid; if (critical) *critical = e->critical; if (der) *der = e->der; if (derlen) *derlen = e->derlen; return 0; } /* Return the authorityKeyIdentifier in r_name and r_serial or in r_keyID. GPG_ERR_NO_DATA is returned if no authorityKeyIdentifier or only one using the keyIdentifier method is available and R_KEYID is NULL. FIXME: This function shares a lot of code with the one in cert.c */ gpg_error_t ksba_crl_get_auth_key_id (ksba_crl_t crl, ksba_sexp_t *r_keyid, ksba_name_t *r_name, ksba_sexp_t *r_serial) { gpg_error_t err; size_t derlen; const unsigned char *der; const unsigned char *keyid_der = NULL; size_t keyid_derlen = 0; struct tag_info ti; char numbuf[30]; size_t numbuflen; crl_extn_t e; if (r_keyid) *r_keyid = NULL; if (!crl || !r_name || !r_serial) return gpg_error (GPG_ERR_INV_VALUE); *r_name = NULL; *r_serial = NULL; for (e=crl->extension_list; e; e = e->next) if (!strcmp (e->oid, oidstr_authorityKeyIdentifier)) break; if (!e) return gpg_error (GPG_ERR_NO_DATA); /* not available */ /* Check that there is only one */ { crl_extn_t e2; for (e2 = e->next; e2; e2 = e2->next) if (!strcmp (e2->oid, oidstr_authorityKeyIdentifier)) return gpg_error (GPG_ERR_DUP_VALUE); } der = e->der; derlen = e->derlen; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (ti.length > derlen) return gpg_error (GPG_ERR_BAD_BER); err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CRL_OBJ); /* We expected a tag. */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); if (ti.tag == 0) { /* keyIdentifier: Just save it away for later use. */ keyid_der = der; keyid_derlen = ti.length; der += ti.length; derlen -= ti.length; /* If the keyid has been requested but no other data follows, we directly jump to the end. */ if (r_keyid && !derlen) goto build_keyid; if (!derlen) return gpg_error (GPG_ERR_NO_DATA); /* not available */ err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CRL_OBJ); /* we expected a tag */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); } if (ti.tag != 1 || !derlen) return gpg_error (GPG_ERR_INV_CRL_OBJ); err = _ksba_name_new_from_der (r_name, der, ti.length); if (err) return err; der += ti.length; derlen -= ti.length; /* Fixme: we should release r_name before returning on error */ err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CRL_OBJ); /* we expected a tag */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); if (ti.tag != 2 || !derlen) return gpg_error (GPG_ERR_INV_CRL_OBJ); sprintf (numbuf,"(%u:", (unsigned int)ti.length); numbuflen = strlen (numbuf); *r_serial = xtrymalloc (numbuflen + ti.length + 2); if (!*r_serial) return gpg_error_from_errno (errno); strcpy (*r_serial, numbuf); memcpy (*r_serial+numbuflen, der, ti.length); (*r_serial)[numbuflen + ti.length] = ')'; (*r_serial)[numbuflen + ti.length + 1] = 0; build_keyid: if (r_keyid && keyid_der && keyid_derlen) { sprintf (numbuf,"(%u:", (unsigned int)keyid_derlen); numbuflen = strlen (numbuf); *r_keyid = xtrymalloc (numbuflen + keyid_derlen + 2); if (!*r_keyid) return gpg_error (GPG_ERR_ENOMEM); strcpy (*r_keyid, numbuf); memcpy (*r_keyid+numbuflen, keyid_der, keyid_derlen); (*r_keyid)[numbuflen + keyid_derlen] = ')'; (*r_keyid)[numbuflen + keyid_derlen + 1] = 0; } return 0; } /* Return the optional crlNumber in NUMBER or GPG_ERR_NO_DATA if it is not available. Caller must release NUMBER if the fuction retruned with success. */ gpg_error_t ksba_crl_get_crl_number (ksba_crl_t crl, ksba_sexp_t *number) { gpg_error_t err; size_t derlen; const unsigned char *der; struct tag_info ti; char numbuf[30]; size_t numbuflen; crl_extn_t e; if (!crl || !number) return gpg_error (GPG_ERR_INV_VALUE); *number = NULL; for (e=crl->extension_list; e; e = e->next) if (!strcmp (e->oid, oidstr_crlNumber)) break; if (!e) return gpg_error (GPG_ERR_NO_DATA); /* not available */ /* Check that there is only one. */ { crl_extn_t e2; for (e2 = e->next; e2; e2 = e2->next) if (!strcmp (e2->oid, oidstr_crlNumber)) return gpg_error (GPG_ERR_DUP_VALUE); } der = e->der; derlen = e->derlen; err = parse_integer (&der, &derlen, &ti); if (err) return err; sprintf (numbuf,"(%u:", (unsigned int)ti.length); numbuflen = strlen (numbuf); *number = xtrymalloc (numbuflen + ti.length + 2); if (!*number) return gpg_error_from_errno (errno); strcpy (*number, numbuf); memcpy (*number+numbuflen, der, ti.length); (*number)[numbuflen + ti.length] = ')'; (*number)[numbuflen + ti.length + 1] = 0; return 0; } /** * ksba_crl_get_update_times: * @crl: CRL object * @this: Returns the thisUpdate value * @next: Returns the nextUpdate value. * * THIS and NEXT may be given as NULL if the value is not required. * Return value: 0 on success or an error code **/ gpg_error_t ksba_crl_get_update_times (ksba_crl_t crl, ksba_isotime_t this, ksba_isotime_t next) { if (this) *this = 0; if (next) *next = 0; if (!crl) return gpg_error (GPG_ERR_INV_VALUE); if (!*crl->this_update) return gpg_error (GPG_ERR_INV_TIME); if (this) _ksba_copy_time (this, crl->this_update); if (next) _ksba_copy_time (next, crl->next_update); return 0; } /** * ksba_crl_get_item: * @crl: CRL object * @r_serial: Returns a S-exp with the serial number; caller must free. * @r_revocation_date: Returns the recocation date * @r_reason: Return the reason for revocation * * Return the serial number, revocation time and reason of the current * item. Any of these arguments may be passed as %NULL if the value * is not of interest. This function should be used after the parse * function came back with %KSBA_SR_GOT_ITEM. For efficiency reasons * the function should be called only once, the implementation may * return an error for the second call. * * Return value: 0 in success or an error code. **/ gpg_error_t ksba_crl_get_item (ksba_crl_t crl, ksba_sexp_t *r_serial, ksba_isotime_t r_revocation_date, ksba_crl_reason_t *r_reason) { if (r_revocation_date) *r_revocation_date = 0; if (!crl) return gpg_error (GPG_ERR_INV_VALUE); if (r_serial) { if (!crl->item.serial) return gpg_error (GPG_ERR_NO_DATA); *r_serial = crl->item.serial; crl->item.serial = NULL; } if (r_revocation_date) _ksba_copy_time (r_revocation_date, crl->item.revocation_date); if (r_reason) *r_reason = crl->item.reason; return 0; } /** * ksba_crl_get_sig_val: * @crl: CRL object * * Return the actual signature in a format suitable to be used as * input to Libgcrypt's verification function. The caller must free * the returned string. * * Return value: NULL or a string with an S-Exp. **/ ksba_sexp_t ksba_crl_get_sig_val (ksba_crl_t crl) { ksba_sexp_t p; if (!crl) return NULL; if (!crl->sigval) return NULL; p = crl->sigval; crl->sigval = NULL; return p; } /* Parser functions */ /* read one byte */ static int read_byte (ksba_reader_t reader) { unsigned char buf; size_t nread; int rc; do rc = ksba_reader_read (reader, &buf, 1, &nread); while (!rc && !nread); return rc? -1: buf; } /* read COUNT bytes into buffer. Return 0 on success */ static int read_buffer (ksba_reader_t reader, char *buffer, size_t count) { size_t nread; while (count) { if (ksba_reader_read (reader, buffer, count, &nread)) return -1; buffer += nread; count -= nread; } return 0; } /* Create a new decoder and run it for the given element */ /* Fixme: this code is duplicated from cms-parser.c */ static gpg_error_t create_and_run_decoder (ksba_reader_t reader, const char *elem_name, AsnNode *r_root, unsigned char **r_image, size_t *r_imagelen) { gpg_error_t err; ksba_asn_tree_t crl_tree; BerDecoder decoder; err = ksba_asn_create_tree ("tmttv2", &crl_tree); if (err) return err; decoder = _ksba_ber_decoder_new (); if (!decoder) { ksba_asn_tree_release (crl_tree); return gpg_error (GPG_ERR_ENOMEM); } err = _ksba_ber_decoder_set_reader (decoder, reader); if (err) { ksba_asn_tree_release (crl_tree); _ksba_ber_decoder_release (decoder); return err; } err = _ksba_ber_decoder_set_module (decoder, crl_tree); if (err) { ksba_asn_tree_release (crl_tree); _ksba_ber_decoder_release (decoder); return err; } err = _ksba_ber_decoder_decode (decoder, elem_name, 0, r_root, r_image, r_imagelen); _ksba_ber_decoder_release (decoder); ksba_asn_tree_release (crl_tree); return err; } /* Parse the extension in the buffer DER or length DERLEN and return the result in OID, CRITICAL, OFF and LEN. */ static gpg_error_t parse_one_extension (const unsigned char *der, size_t derlen, char **oid, int *critical, size_t *off, size_t *len) { gpg_error_t err; struct tag_info ti; const unsigned char *start = der; *oid = NULL; *critical = 0; *off = *len = 0; /* Extension ::= SEQUENCE { extnID OBJECT IDENTIFIER, critical BOOLEAN DEFAULT FALSE, extnValue OCTET STRING } */ err = parse_sequence (&der, &derlen, &ti); if (err) goto failure; err = parse_object_id_into_str (&der, &derlen, oid); if (err) goto failure; err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) goto failure; if (ti.length > derlen) return gpg_error (GPG_ERR_BAD_BER); if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_BOOLEAN && !ti.is_constructed) { if (ti.length != 1) goto bad_ber; *critical = !!*der; parse_skip (&der, &derlen, &ti); } else { /* Undo that read. */ der -= ti.nhdr; derlen += ti.nhdr; } err = parse_octet_string (&der, &derlen, &ti); if (err) goto failure; *off = der - start; *len = ti.length; return 0; bad_ber: err = gpg_error (GPG_ERR_BAD_BER); failure: xfree (*oid); *oid = NULL; return err; } /* Store an extension into the context. */ static gpg_error_t store_one_extension (ksba_crl_t crl, const unsigned char *der, size_t derlen) { gpg_error_t err; char *oid; int critical; size_t off, len; crl_extn_t e; err = parse_one_extension (der, derlen, &oid, &critical, &off, &len); if (err) return err; e = xtrymalloc (sizeof *e + len - 1); if (!e) { err = gpg_error_from_errno (errno); xfree (oid); return err; } e->oid = oid; e->critical = critical; e->derlen = len; memcpy (e->der, der + off, len); e->next = crl->extension_list; crl->extension_list = e; return 0; } /* Parse the fixed block at the beginning. We use a custom parser here because our BER decoder is not yet able to stop at certain points */ static gpg_error_t parse_to_next_update (ksba_crl_t crl) { gpg_error_t err; struct tag_info ti; unsigned long outer_len, tbs_len; int outer_ndef, tbs_ndef; int c; unsigned char tmpbuf[500]; /* for OID or algorithmIdentifier */ size_t nread; /* read the outer sequence */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); outer_len = ti.length; outer_ndef = ti.ndef; if (!outer_ndef && outer_len < 10) return gpg_error (GPG_ERR_TOO_SHORT); /* read the tbs sequence */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); HASH (ti.buf, ti.nhdr); if (!outer_ndef) { if (outer_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); /* Triplet header larger than outer sequence */ outer_len -= ti.nhdr; if (!ti.ndef && outer_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); /* Triplet larger than outer sequence */ outer_len -= ti.length; } tbs_len = ti.length; tbs_ndef = ti.ndef; if (!tbs_ndef && tbs_len < 10) return gpg_error (GPG_ERR_TOO_SHORT); /* read the optional version integer */ crl->crl_version = -1; err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_INTEGER) { if ( ti.is_constructed || !ti.length ) return gpg_error (GPG_ERR_INV_CRL_OBJ); HASH (ti.buf, ti.nhdr); if (!tbs_ndef) { if (tbs_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); tbs_len -= ti.nhdr; if (tbs_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); tbs_len -= ti.length; } /* fixme: we should also check the outer data length here and in the follwing code. It might however be easier to to thsi at the end of this sequence */ if (ti.length != 1) return gpg_error (GPG_ERR_UNSUPPORTED_CRL_VERSION); if ( (c=read_byte (crl->reader)) == -1) { err = ksba_reader_error (crl->reader); return err? err : gpg_error (GPG_ERR_GENERAL); } if ( !(c == 0 || c == 1) ) return gpg_error (GPG_ERR_UNSUPPORTED_CRL_VERSION); { unsigned char tmp = c; HASH (&tmp, 1); } crl->crl_version = c; err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; } /* read the algorithm identifier */ if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); if (!tbs_ndef) { if (tbs_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); tbs_len -= ti.nhdr; if (!ti.ndef && tbs_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); tbs_len -= ti.length; } if (ti.nhdr + ti.length >= DIM(tmpbuf)) return gpg_error (GPG_ERR_TOO_LARGE); memcpy (tmpbuf, ti.buf, ti.nhdr); err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length); if (err) return err; HASH (tmpbuf, ti.nhdr+ti.length); xfree (crl->algo.oid); crl->algo.oid = NULL; xfree (crl->algo.parm); crl->algo.parm = NULL; err = _ksba_parse_algorithm_identifier2 (tmpbuf, ti.nhdr+ti.length, &nread, &crl->algo.oid, &crl->algo.parm, &crl->algo.parmlen); if (err) return err; assert (nread <= ti.nhdr + ti.length); if (nread < ti.nhdr + ti.length) return gpg_error (GPG_ERR_TOO_SHORT); /* read the name */ { unsigned long n = ksba_reader_tell (crl->reader); err = create_and_run_decoder (crl->reader, "TMTTv2.CertificateList.tbsCertList.issuer", &crl->issuer.root, &crl->issuer.image, &crl->issuer.imagelen); if (err) return err; /* imagelen might be larger than the valid data (due to read ahead). So we need to get the count from the reader */ n = ksba_reader_tell (crl->reader) - n; if (n > crl->issuer.imagelen) return gpg_error (GPG_ERR_BUG); HASH (crl->issuer.image, n); if (!tbs_ndef) { if (tbs_len < n) return gpg_error (GPG_ERR_BAD_BER); tbs_len -= n; } } /* read the thisUpdate time */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && (ti.tag == TYPE_UTC_TIME || ti.tag == TYPE_GENERALIZED_TIME) && !ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); if (!tbs_ndef) { if (tbs_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); tbs_len -= ti.nhdr; if (!ti.ndef && tbs_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); tbs_len -= ti.length; } if (ti.nhdr + ti.length >= DIM(tmpbuf)) return gpg_error (GPG_ERR_TOO_LARGE); memcpy (tmpbuf, ti.buf, ti.nhdr); err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length); if (err) return err; HASH (tmpbuf, ti.nhdr+ti.length); _ksba_asntime_to_iso (tmpbuf+ti.nhdr, ti.length, ti.tag == TYPE_UTC_TIME, crl->this_update); /* Read the optional nextUpdate time. */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( ti.class == CLASS_UNIVERSAL && (ti.tag == TYPE_UTC_TIME || ti.tag == TYPE_GENERALIZED_TIME) && !ti.is_constructed ) { if (!tbs_ndef) { if (tbs_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); tbs_len -= ti.nhdr; if (!ti.ndef && tbs_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); tbs_len -= ti.length; } if (ti.nhdr + ti.length >= DIM(tmpbuf)) return gpg_error (GPG_ERR_TOO_LARGE); memcpy (tmpbuf, ti.buf, ti.nhdr); err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length); if (err) return err; HASH (tmpbuf, ti.nhdr+ti.length); _ksba_asntime_to_iso (tmpbuf+ti.nhdr, ti.length, ti.tag == TYPE_UTC_TIME, crl->next_update); err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; } /* Read the first sequence tag of the optional SEQ of SEQ. */ if (tbs_ndef || tbs_len) { if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed ) { /* yes, there is one */ HASH (ti.buf, ti.nhdr); if (!tbs_ndef) { if (tbs_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); tbs_len -= ti.nhdr; if (!ti.ndef && tbs_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); tbs_len -= ti.length; } crl->state.have_seqseq = 1; crl->state.seqseq_ndef = ti.ndef; crl->state.seqseq_len = ti.length; /* and read the next */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; } } /* We need to save some stuff for the next round. */ crl->state.ti = ti; crl->state.outer_ndef = outer_ndef; crl->state.outer_len = outer_len; crl->state.tbs_ndef = tbs_ndef; crl->state.tbs_len = tbs_len; return 0; } /* Parse an enumerated value. Note that this code is duplication of the one at ocsp.c. */ static gpg_error_t parse_enumerated (unsigned char const **buf, size_t *len, struct tag_info *ti, size_t maxlen) { gpg_error_t err; err = _ksba_ber_parse_tl (buf, len, ti); if (err) ; else if (!(ti->class == CLASS_UNIVERSAL && ti->tag == TYPE_ENUMERATED && !ti->is_constructed) ) err = gpg_error (GPG_ERR_INV_OBJ); else if (!ti->length) err = gpg_error (GPG_ERR_TOO_SHORT); else if (maxlen && ti->length > maxlen) err = gpg_error (GPG_ERR_TOO_LARGE); else if (ti->length > *len) err = gpg_error (GPG_ERR_BAD_BER); return err; } /* Store an entry extension into the current item. */ static gpg_error_t store_one_entry_extension (ksba_crl_t crl, const unsigned char *der, size_t derlen) { gpg_error_t err; char *oid; int critical; size_t off, len; err = parse_one_extension (der, derlen, &oid, &critical, &off, &len); if (err) return err; if (!strcmp (oid, oidstr_crlReason)) { struct tag_info ti; const unsigned char *buf = der+off; size_t mylen = len; err = parse_enumerated (&buf, &mylen, &ti, 1); if (err) return err; /* Note that we OR the values so that in case this extension is repeated we can track all reason codes. */ switch (*buf) { case 0: crl->item.reason |= KSBA_CRLREASON_UNSPECIFIED; break; case 1: crl->item.reason |= KSBA_CRLREASON_KEY_COMPROMISE; break; case 2: crl->item.reason |= KSBA_CRLREASON_CA_COMPROMISE; break; case 3: crl->item.reason |= KSBA_CRLREASON_AFFILIATION_CHANGED; break; case 4: crl->item.reason |= KSBA_CRLREASON_SUPERSEDED; break; case 5: crl->item.reason |= KSBA_CRLREASON_CESSATION_OF_OPERATION; break; case 6: crl->item.reason |= KSBA_CRLREASON_CERTIFICATE_HOLD; break; case 8: crl->item.reason |= KSBA_CRLREASON_REMOVE_FROM_CRL; break; case 9: crl->item.reason |= KSBA_CRLREASON_PRIVILEGE_WITHDRAWN; break; case 10: crl->item.reason |= KSBA_CRLREASON_AA_COMPROMISE; break; default: crl->item.reason |= KSBA_CRLREASON_OTHER; break; } } if (!strcmp (oid, oidstr_certificateIssuer)) { /* FIXME: We need to implement this. */ } else if (critical) err = gpg_error (GPG_ERR_UNKNOWN_CRIT_EXTN); xfree (oid); return err; } /* Parse the revokedCertificates SEQUENCE of SEQUENCE using a custom parser for efficiency and return after each entry */ static gpg_error_t parse_crl_entry (ksba_crl_t crl, int *got_entry) { gpg_error_t err; struct tag_info ti = crl->state.ti; unsigned long seqseq_len= crl->state.seqseq_len; int seqseq_ndef = crl->state.seqseq_ndef; unsigned long len; int ndef; unsigned char tmpbuf[4096]; /* for time, serial number and extensions */ char numbuf[22]; int numbuflen; /* Check the length to see whether we are at the end of the seq but do this only when we know that we have this optional seq of seq. */ if (!crl->state.have_seqseq) return 0; /* ready (no entries at all) */ if (!seqseq_ndef && !seqseq_len) return 0; /* ready */ /* if this is not a SEQUENCE the CRL is invalid */ if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); HASH (ti.buf, ti.nhdr); if (!seqseq_ndef) { if (seqseq_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); seqseq_len -= ti.nhdr; if (!ti.ndef && seqseq_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); seqseq_len -= ti.length; } ndef = ti.ndef; len = ti.length; /* get the serial number */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_INTEGER && !ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); if (!ndef) { if (len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); len -= ti.nhdr; if (!ti.ndef && len < ti.length) return gpg_error (GPG_ERR_BAD_BER); len -= ti.length; } if (ti.nhdr + ti.length >= DIM(tmpbuf)) return gpg_error (GPG_ERR_TOO_LARGE); memcpy (tmpbuf, ti.buf, ti.nhdr); err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length); if (err) return err; HASH (tmpbuf, ti.nhdr+ti.length); xfree (crl->item.serial); sprintf (numbuf,"(%u:", (unsigned int)ti.length); numbuflen = strlen (numbuf); crl->item.serial = xtrymalloc (numbuflen + ti.length + 2); if (!crl->item.serial) return gpg_error (GPG_ERR_ENOMEM); strcpy (crl->item.serial, numbuf); memcpy (crl->item.serial+numbuflen, tmpbuf+ti.nhdr, ti.length); crl->item.serial[numbuflen + ti.length] = ')'; crl->item.serial[numbuflen + ti.length + 1] = 0; crl->item.reason = 0; /* get the revocation time */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && (ti.tag == TYPE_UTC_TIME || ti.tag == TYPE_GENERALIZED_TIME) && !ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); if (!ndef) { if (len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); len -= ti.nhdr; if (!ti.ndef && len < ti.length) return gpg_error (GPG_ERR_BAD_BER); len -= ti.length; } if (ti.nhdr + ti.length >= DIM(tmpbuf)) return gpg_error (GPG_ERR_TOO_LARGE); memcpy (tmpbuf, ti.buf, ti.nhdr); err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length); if (err) return err; HASH (tmpbuf, ti.nhdr+ti.length); _ksba_asntime_to_iso (tmpbuf+ti.nhdr, ti.length, ti.tag == TYPE_UTC_TIME, crl->item.revocation_date); /* if there is still space we must parse the optional entryExtensions */ if (ndef) return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); else if (len) { /* read the outer sequence */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); HASH (ti.buf, ti.nhdr); if (len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); len -= ti.nhdr; if (len < ti.length) return gpg_error (GPG_ERR_BAD_BER); /* now loop over the extensions */ while (len) { err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); if (len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); len -= ti.nhdr; if (len < ti.length) return gpg_error (GPG_ERR_BAD_BER); len -= ti.length; if (ti.nhdr + ti.length >= DIM(tmpbuf)) return gpg_error (GPG_ERR_TOO_LARGE); memcpy (tmpbuf, ti.buf, ti.nhdr); err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length); if (err) return err; HASH (tmpbuf, ti.nhdr+ti.length); err = store_one_entry_extension (crl, tmpbuf, ti.nhdr+ti.length); if (err) return err; } } /* read ahead */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; *got_entry = 1; /* Fixme: the seqseq length is not correct if any element was ndef'd */ crl->state.ti = ti; crl->state.seqseq_ndef = seqseq_ndef; crl->state.seqseq_len = seqseq_len; return 0; } /* This function is used when a [0] tag was encountered to read the crlExtensions */ static gpg_error_t parse_crl_extensions (ksba_crl_t crl) { gpg_error_t err; struct tag_info ti = crl->state.ti; unsigned long ext_len, len; unsigned char tmpbuf[4096]; /* for extensions */ /* if we do not have a tag [0] we are done with this */ if (!(ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed)) return 0; if (ti.ndef) return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); HASH (ti.buf, ti.nhdr); ext_len = ti.length; /* read the outer sequence */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); HASH (ti.buf, ti.nhdr); if (ext_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); ext_len -= ti.nhdr; if (ext_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); len = ti.length; /* now loop over the extensions */ while (len) { err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); if (len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); len -= ti.nhdr; if (len < ti.length) return gpg_error (GPG_ERR_BAD_BER); len -= ti.length; if (ti.nhdr + ti.length >= DIM(tmpbuf)) return gpg_error (GPG_ERR_TOO_LARGE); /* fixme use a larger buffer if the extension does not fit into tmpbuf */ memcpy (tmpbuf, ti.buf, ti.nhdr); err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length); if (err) return err; HASH (tmpbuf, ti.nhdr+ti.length); err = store_one_extension (crl, tmpbuf, ti.nhdr+ti.length); if (err) return err; } /* read ahead */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; crl->state.ti = ti; return 0; } /* Parse the signatureAlgorithm and the signature */ static gpg_error_t parse_signature (ksba_crl_t crl) { gpg_error_t err; struct tag_info ti = crl->state.ti; unsigned char tmpbuf[2048]; /* for the sig algo and bitstr */ size_t n, n2; /* We do read the stuff into a temporary buffer so that we can apply our parsing function for this structure */ /* read the algorithmIdentifier sequence */ if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); if (ti.ndef) return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); n = ti.nhdr + ti.length; if (n >= DIM(tmpbuf)) return gpg_error (GPG_ERR_TOO_LARGE); memcpy (tmpbuf, ti.buf, ti.nhdr); err = read_buffer (crl->reader, tmpbuf+ti.nhdr, ti.length); if (err) return err; /* and append the bit string */ err = _ksba_ber_read_tl (crl->reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_BIT_STRING && !ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CRL_OBJ); n2 = ti.nhdr + ti.length; if (n + n2 >= DIM(tmpbuf)) return gpg_error (GPG_ERR_TOO_LARGE); memcpy (tmpbuf+n, ti.buf, ti.nhdr); err = read_buffer (crl->reader, tmpbuf+n+ti.nhdr, ti.length); if (err) return err; /* now parse it */ xfree (crl->sigval); crl->sigval = NULL; return _ksba_sigval_to_sexp (tmpbuf, n + n2, &crl->sigval); } /* The actual parser which should be used with a new CRL object and run in a loop until the the KSBA_SR_READY is encountered */ gpg_error_t ksba_crl_parse (ksba_crl_t crl, ksba_stop_reason_t *r_stopreason) { enum { sSTART, sCRLENTRY, sCRLEXT, sERROR } state = sERROR; ksba_stop_reason_t stop_reason; gpg_error_t err = 0; int got_entry = 0; if (!crl || !r_stopreason) return gpg_error (GPG_ERR_INV_VALUE); if (!crl->any_parse_done) { /* first time initialization of the stop reason */ *r_stopreason = 0; crl->any_parse_done = 1; } /* Calculate state from last reason */ stop_reason = *r_stopreason; *r_stopreason = KSBA_SR_RUNNING; switch (stop_reason) { case 0: state = sSTART; break; case KSBA_SR_BEGIN_ITEMS: case KSBA_SR_GOT_ITEM: state = sCRLENTRY; break; case KSBA_SR_END_ITEMS: state = sCRLEXT; break; case KSBA_SR_RUNNING: err = gpg_error (GPG_ERR_INV_STATE); break; default: err = gpg_error (GPG_ERR_BUG); break; } if (err) return err; /* Do the action */ switch (state) { case sSTART: err = parse_to_next_update (crl); break; case sCRLENTRY: err = parse_crl_entry (crl, &got_entry); break; case sCRLEXT: err = parse_crl_extensions (crl); if (!err) { if (crl->hash_fnc && crl->hashbuf.used) crl->hash_fnc (crl->hash_fnc_arg, crl->hashbuf.buffer, crl->hashbuf.used); crl->hashbuf.used = 0; err = parse_signature (crl); } break; default: err = gpg_error (GPG_ERR_INV_STATE); break; } if (err) return err; /* Calculate new stop reason */ switch (state) { case sSTART: stop_reason = KSBA_SR_BEGIN_ITEMS; break; case sCRLENTRY: stop_reason = got_entry? KSBA_SR_GOT_ITEM : KSBA_SR_END_ITEMS; break; case sCRLEXT: stop_reason = KSBA_SR_READY; break; default: break; } *r_stopreason = stop_reason; return 0; } diff --git a/src/gen-help.h b/src/gen-help.h index c0a3776..05641f9 100644 --- a/src/gen-help.h +++ b/src/gen-help.h @@ -1,95 +1,97 @@ /* gen-help.c - Helper functions used by build time tools * Copyright (C) 2010 g10 Code GmbH * * This file is part of KSBA. * * KSBA 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 Fountion; either version 3 of the License, or * (at your option) any later version. * * KSBA 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, see . */ /* This header has definitions used by programs which are only run on the build platform as part of the build process. They need to be plain ISO C and don't make use of any information gathered from the configure run. */ #ifndef GEN_HELP_H #define GEN_HELP_H #ifndef BUILD_GENTOOLS #error file may only be used for build time tools #endif void *xmalloc (size_t n); void *xcalloc (size_t n, size_t m); void *xrealloc (void *mem, size_t n); char *xstrdup (const char *str); void xfree (void *a); #define xtrymalloc(a) malloc ((a)) char *gen_help_stpcpy (char *a, const char *b); #define stpcpy(a, b) gen_help_stpcpy ((a), (b)) #define DIM(v) (sizeof(v)/sizeof((v)[0])) #define DIMof(type,member) DIM(((type *)0)->member) #ifndef STR # define STR(v) #v #endif #ifndef STR2 # define STR2(v) STR(v) #endif #define return_if_fail(expr) do { \ if (!(expr)) { \ fprintf (stderr, "%s:%d: assertion `%s' failed\n", \ __FILE__, __LINE__, #expr ); \ return; \ } } while (0) #define return_null_if_fail(expr) do { \ if (!(expr)) { \ fprintf (stderr, "%s:%d: assertion `%s' failed\n", \ __FILE__, __LINE__, #expr ); \ return NULL; \ } } while (0) #define return_val_if_fail(expr,val) do { \ if (!(expr)) { \ fprintf (stderr, "%s:%d: assertion `%s' failed\n", \ __FILE__, __LINE__, #expr ); \ return (val); \ } } while (0) #define never_reached() do { \ fprintf (stderr, "%s:%d: oops; should never get here\n", \ __FILE__, __LINE__ ); \ } while (0) /* Replacement for gpg_error.h stuff. */ #define GPG_ERR_GENERAL 1 #define GPG_ERR_SYNTAX 29 #define GPG_ERR_INV_VALUE 55 #define GPG_ERR_BUG 59 #define GPG_ERR_ELEMENT_NOT_FOUND 136 #define GPG_ERR_IDENTIFIER_NOT_FOUND 137 #define gpg_error(a) (a) #define gpg_error_from_syserror() (GPG_ERR_GENERAL); const char *gpg_strerror (int err); /* Duplicated type definitions from ksba.h. */ typedef struct ksba_asn_tree_s *ksba_asn_tree_t; - +int ksba_asn_parse_file (const char *filename, ksba_asn_tree_t *result, + int debug); +void ksba_asn_tree_dump (ksba_asn_tree_t tree, const char *name, FILE *fp); #endif /*GEN_HELP_H*/ diff --git a/tests/t-dnparser.c b/tests/t-dnparser.c index ef4ab5d..f100888 100644 --- a/tests/t-dnparser.c +++ b/tests/t-dnparser.c @@ -1,172 +1,172 @@ /* t-dnparser.c - basic test for the DN parser * Copyright (C) 2002, 2006 g10 Code GmbH * * This file is part of KSBA. * * KSBA 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; either version 3 of the License, or * (at your option) any later version. * * KSBA 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, see . */ #include #include #include #include #include #include #include "../src/ksba.h" #include "t-common.h" static void test_0 (void) { static char *good_strings[] = { "C=de,O=g10 Code,OU=qa,CN=Pépé le Moko", "C= de, O=g10 Code , OU=qa ,CN=Pépé le Moko", "CN=www.gnupg.org", " CN=www.gnupg.org ", "C=fr,L=Paris,CN=Julien Duvivier,EMAIL=julien@example.org", NULL }; gpg_error_t err; int i; unsigned char *buf; size_t off, len; for (i=0; good_strings[i]; i++) { err = ksba_dn_str2der (good_strings[i], &buf, &len); if (err) { fprintf (stderr, "%s:%d: ksba_dn_str2der failed for `%s': %s\n", __FILE__,__LINE__, good_strings[i], gpg_strerror (err)); exit (1); } err = ksba_dn_teststr (good_strings[i], 0, &off, &len); if (err) { fprintf (stderr, "%s:%d: ksba_dn_teststr failed for `%s': %s\n", __FILE__,__LINE__, good_strings[i], gpg_strerror (err)); exit (1); } xfree (buf); } } static void test_1 (void) { static char *empty_elements[] = { "C=de,O=foo,OU=,CN=joe", "C=de,O=foo,OU= ,CN=joe", "C=de,O=foo,OU=\"\" ,CN=joe", "C=de,O=foo,OU=", "C=de,O=foo,OU= ", "C=,O=foo,OU=bar ", "C = ,O=foo,OU=bar ", "C=", NULL }; gpg_error_t err; int i; unsigned char *buf; size_t off, len; for (i=0; empty_elements[i]; i++) { err = ksba_dn_str2der (empty_elements[i], &buf, &len); if (gpg_err_code (err) != GPG_ERR_SYNTAX) fail ("empty element not detected"); err = ksba_dn_teststr (empty_elements[i], 0, &off, &len); if (!err) fail ("ksba_dn_teststr returned no error"); printf ("string ->%s<- error at %lu.%lu (%.*s)\n", empty_elements[i], (unsigned long)off, (unsigned long)len, (int)len, empty_elements[i]+off); xfree (buf); } } static void test_2 (void) { static char *invalid_labels[] = { "C=de,FOO=something,O=bar", "Y=foo, C=baz", NULL }; gpg_error_t err; int i; unsigned char *buf; size_t off, len; for (i=0; invalid_labels[i]; i++) { err = ksba_dn_str2der (invalid_labels[i], &buf, &len); if (gpg_err_code (err) != GPG_ERR_UNKNOWN_NAME) fail ("invalid label not detected"); err = ksba_dn_teststr (invalid_labels[i], 0, &off, &len); if (!err) fail ("ksba_dn_test_str returned no error"); printf ("string ->%s<- error at %lu.%lu (%.*s)\n", invalid_labels[i], (unsigned long)off, (unsigned long)len, (int)len, invalid_labels[i]+off); xfree (buf); } } int main (int argc, char **argv) { char inputbuf[4096]; unsigned char *buf; size_t len; gpg_error_t err; if (argc == 2 && !strcmp (argv[1], "--to-str") ) { /* Read the DER encoded DN from stdin write the string to stdout */ - fread (inputbuf, 1, sizeof inputbuf, stdin); + len = fread (inputbuf, 1, sizeof inputbuf, stdin); if (!feof (stdin)) fail ("read error or input too large"); fail ("not yet implemented"); } else if (argc == 2 && !strcmp (argv[1], "--to-der") ) { /* Read the String from stdin write the DER encoding to stdout */ - fread (inputbuf, 1, sizeof inputbuf, stdin); + len = fread (inputbuf, 1, sizeof inputbuf, stdin); if (!feof (stdin)) fail ("read error or input too large"); err = ksba_dn_str2der (inputbuf, &buf, &len); fail_if_err (err); fwrite (buf, len, 1, stdout); } else if (argc == 1) { test_0 (); test_1 (); test_2 (); } else { fprintf (stderr, "usage: t-dnparser [--to-str|--to-der]\n"); return 1; } return 0; }