1 /***************************************************************************
2  * Copyright (c) 2024 Microsoft Corporation
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the MIT License which is available at
6  * https://opensource.org/licenses/MIT.
7  *
8  * SPDX-License-Identifier: MIT
9  **************************************************************************/
10 
11 
12 /**************************************************************************/
13 /**************************************************************************/
14 /**                                                                       */
15 /** NetX Secure Component                                                 */
16 /**                                                                       */
17 /**    X.509 Digital Certificates                                         */
18 /**                                                                       */
19 /**************************************************************************/
20 /**************************************************************************/
21 
22 #define NX_SECURE_SOURCE_CODE
23 
24 #include "nx_secure_x509.h"
25 
26 static UINT _nx_secure_x509_extract_name_oid_data(const UCHAR *buffer, UINT oid, ULONG length,
27                                                   UINT *bytes_processed,
28                                                   NX_SECURE_X509_DISTINGUISHED_NAME *name);
29 
30 /**************************************************************************/
31 /*                                                                        */
32 /*  FUNCTION                                               RELEASE        */
33 /*                                                                        */
34 /*    _nx_secure_x509_distinguished_name_parse            PORTABLE C      */
35 /*                                                           6.1.6        */
36 /*  AUTHOR                                                                */
37 /*                                                                        */
38 /*    Timothy Stapko, Microsoft Corporation                               */
39 /*                                                                        */
40 /*  DESCRIPTION                                                           */
41 /*                                                                        */
42 /*    This function parses a DER-encoded X.509 Distinguished Name in the  */
43 /*    supplied buffer, placing the parsed data into a structure that is   */
44 /*    returned to the caller.                                             */
45 /*                                                                        */
46 /*  INPUT                                                                 */
47 /*                                                                        */
48 /*    buffer                                Pointer to data to be parsed  */
49 /*    length                                Length of data in buffer      */
50 /*    bytes_processed                       Return number of bytes parsed */
51 /*    name                                  Return parsed name structure  */
52 /*                                                                        */
53 /*  OUTPUT                                                                */
54 /*                                                                        */
55 /*    status                                Completion status             */
56 /*                                                                        */
57 /*  CALLS                                                                 */
58 /*                                                                        */
59 /*    _nx_secure_x509_asn1_tlv_block_parse  Parse ASN.1 block             */
60 /*    _nx_secure_x509_extract_name_oid_data Extract Distinguished Name    */
61 /*    _nx_secure_x509_oid_parse             Parse OID in certificate      */
62 /*                                                                        */
63 /*  CALLED BY                                                             */
64 /*                                                                        */
65 /*    _nx_secure_x509_crl_issuer_parse      Parse issuer in crl           */
66 /*    _nx_secure_x509_parse_issuer          Parse issuer in certificate   */
67 /*    _nx_secure_x509_parse_subject         Parse subject in certificate  */
68 /*                                                                        */
69 /*  RELEASE HISTORY                                                       */
70 /*                                                                        */
71 /*    DATE              NAME                      DESCRIPTION             */
72 /*                                                                        */
73 /*  05-19-2020     Timothy Stapko           Initial Version 6.0           */
74 /*  09-30-2020     Timothy Stapko           Modified comment(s),          */
75 /*                                            resulting in version 6.1    */
76 /*  04-02-2021     Timothy Stapko           Modified comment(s),          */
77 /*                                            removed dependency on TLS,  */
78 /*                                            resulting in version 6.1.6  */
79 /*                                                                        */
80 /**************************************************************************/
_nx_secure_x509_distinguished_name_parse(const UCHAR * buffer,UINT length,UINT * bytes_processed,NX_SECURE_X509_DISTINGUISHED_NAME * name)81 UINT _nx_secure_x509_distinguished_name_parse(const UCHAR *buffer, UINT length, UINT *bytes_processed,
82                                               NX_SECURE_X509_DISTINGUISHED_NAME *name)
83 {
84 USHORT       tlv_type;
85 USHORT       tlv_type_class;
86 ULONG        tlv_length;
87 UINT         bytes;
88 const UCHAR *tlv_data;
89 ULONG        header_length;
90 UINT         status;
91 UINT         current_index;
92 const UCHAR *sequence_ptr;
93 ULONG        sequence_length;
94 UINT         oid;
95 
96     /* Parse distinguished name information including Common Name, country/region, organization, etc... */
97     /*
98      *          Sequence (16): Name information - parsed above this point in hierarchy
99      *             Set (17): Information (multiple sets here - one per OID)
100      *                 Sequence (16): Information (multiple sequences here?)
101      *                     ASN.1 OID (6)   (e.g. Country/Region)
102      *                     ASN.1 String (UTF8-12, Printable-19) (e.g. "US")
103      *             ...
104      */
105 
106     current_index = 0;
107     *bytes_processed = 0;
108 
109     /*  Continue parsing ASN.1 sets until we reach the end of the surrounding sequence, determined
110      *  by the "length" parameter. */
111     while (length > 0)
112     {
113         /*  First, parse the set. */
114         status = _nx_secure_x509_asn1_tlv_block_parse(&buffer[current_index], (ULONG *)&length, &tlv_type, &tlv_type_class, &tlv_length, &tlv_data, &header_length);
115 
116         /*  Make sure we parsed the block alright. */
117         if (status != 0)
118         {
119             return(status);
120         }
121 
122         if (tlv_type != NX_SECURE_ASN_TAG_SET || tlv_type_class != NX_SECURE_ASN_TAG_CLASS_UNIVERSAL)
123         {
124             return(NX_SECURE_X509_UNEXPECTED_ASN1_TAG);
125         }
126 
127         /*  Advance the top-level buffer pointer to the next SET entry. */
128         current_index += (header_length + tlv_length);
129         *bytes_processed += (header_length + tlv_length);
130         sequence_length = tlv_length;
131 
132         /*  Next, parse the sequence. */
133         status = _nx_secure_x509_asn1_tlv_block_parse(tlv_data, &sequence_length, &tlv_type, &tlv_type_class, &tlv_length, &tlv_data, &header_length);
134 
135         /*  Make sure we parsed the block alright. */
136         if (status != 0)
137         {
138             return(status);
139         }
140 
141         if (tlv_type != NX_SECURE_ASN_TAG_SEQUENCE || tlv_type_class != NX_SECURE_ASN_TAG_CLASS_UNIVERSAL)
142         {
143             return(NX_SECURE_X509_UNEXPECTED_ASN1_TAG);
144         }
145 
146         /*  Save the sequence for the calls below. */
147         sequence_ptr = tlv_data;
148         sequence_length = tlv_length;
149 
150         /*  Now we have the actual data. First extract the OID.*/
151         status = _nx_secure_x509_asn1_tlv_block_parse(sequence_ptr, &sequence_length, &tlv_type, &tlv_type_class, &tlv_length, &tlv_data, &header_length);
152 
153         /*  Make sure we parsed the block alright. */
154         if (status != 0)
155         {
156             return(status);
157         }
158 
159         if (tlv_type != NX_SECURE_ASN_TAG_OID || tlv_type_class != NX_SECURE_ASN_TAG_CLASS_UNIVERSAL)
160         {
161             return(NX_SECURE_X509_UNEXPECTED_ASN1_TAG);
162         }
163 
164         /* The OID is in the data we extracted. */
165         _nx_secure_x509_oid_parse(tlv_data, tlv_length, &oid);
166 
167         /* Extract the data in the block following the OID. Use the remaining length of the sequence
168          * for the length of the data going in. */
169         status = _nx_secure_x509_extract_name_oid_data(&tlv_data[tlv_length], oid, sequence_length, &bytes, name);
170 
171         if (status != 0)
172         {
173             return(status);
174         }
175     }
176 
177     /* All done parsing id info! */
178     return(NX_SECURE_X509_SUCCESS);
179 }
180 
181 
182 /* This function extracts name identification data from the certificate distinguished name fields (subject and issuer)
183    by matching OIDs and populating the correct fields in the distinguished name structure. */
184 /**************************************************************************/
185 /*                                                                        */
186 /*  FUNCTION                                               RELEASE        */
187 /*                                                                        */
188 /*    _nx_secure_x509_extract_name_oid_data               PORTABLE C      */
189 /*                                                           6.1          */
190 /*  AUTHOR                                                                */
191 /*                                                                        */
192 /*    Timothy Stapko, Microsoft Corporation                               */
193 /*                                                                        */
194 /*  DESCRIPTION                                                           */
195 /*                                                                        */
196 /*    This function parses a DER-encoded X.509 Distinguished Name in the  */
197 /*    supplied buffer, placing the parsed data into a structure that is   */
198 /*    returned to the caller.                                             */
199 /*                                                                        */
200 /*  INPUT                                                                 */
201 /*                                                                        */
202 /*    buffer                                Pointer to data to be parsed  */
203 /*    oid                                   OID data to be parsed         */
204 /*    length                                Length of data in buffer      */
205 /*    bytes_processed                       Return number of bytes parsed */
206 /*    name                                  Return parsed name structure  */
207 /*                                                                        */
208 /*  OUTPUT                                                                */
209 /*                                                                        */
210 /*    status                                Completion status             */
211 /*                                                                        */
212 /*  CALLS                                                                 */
213 /*                                                                        */
214 /*    _nx_secure_x509_asn1_tlv_block_parse  Parse ASN.1 block             */
215 /*                                                                        */
216 /*  CALLED BY                                                             */
217 /*                                                                        */
218 /*    _nx_secure_x509_distinguished_name_parse                            */
219 /*                                          Parse Distinguished Name      */
220 /*                                                                        */
221 /*  RELEASE HISTORY                                                       */
222 /*                                                                        */
223 /*    DATE              NAME                      DESCRIPTION             */
224 /*                                                                        */
225 /*  05-19-2020     Timothy Stapko           Initial Version 6.0           */
226 /*  09-30-2020     Timothy Stapko           Modified comment(s),          */
227 /*                                            resulting in version 6.1    */
228 /*                                                                        */
229 /**************************************************************************/
_nx_secure_x509_extract_name_oid_data(const UCHAR * buffer,UINT oid,ULONG length,UINT * bytes_processed,NX_SECURE_X509_DISTINGUISHED_NAME * name)230 static UINT _nx_secure_x509_extract_name_oid_data(const UCHAR *buffer, UINT oid, ULONG length,
231                                                   UINT *bytes_processed,
232                                                   NX_SECURE_X509_DISTINGUISHED_NAME *name)
233 {
234 USHORT       tlv_type;
235 USHORT       tlv_type_class;
236 ULONG        tlv_length;
237 const UCHAR *tlv_data;
238 ULONG        header_length;
239 UINT         status;
240 
241     status = _nx_secure_x509_asn1_tlv_block_parse(buffer, &length, &tlv_type, &tlv_type_class, &tlv_length, &tlv_data, &header_length);
242     if (status != 0)
243     {
244         return(status);
245     }
246 
247     *bytes_processed = (header_length + tlv_length);
248 
249     switch (oid)
250     {
251     case NX_SECURE_TLS_X509_TYPE_COMMON_NAME:
252         name -> nx_secure_x509_common_name = tlv_data;
253         name -> nx_secure_x509_common_name_length = (USHORT)tlv_length;
254         break;
255     case NX_SECURE_TLS_X509_TYPE_COUNTRY:
256         name -> nx_secure_x509_country = tlv_data;
257         name -> nx_secure_x509_country_length = (USHORT)tlv_length;
258         break;
259     case NX_SECURE_TLS_X509_TYPE_STATE:
260         name -> nx_secure_x509_state = tlv_data;
261         name -> nx_secure_x509_state_length = (USHORT)tlv_length;
262         break;
263     case NX_SECURE_TLS_X509_TYPE_ORGANIZATION:
264         name -> nx_secure_x509_organization = tlv_data;
265         name -> nx_secure_x509_organization_length = (USHORT)tlv_length;
266         break;
267     case NX_SECURE_TLS_X509_TYPE_ORG_UNIT:
268         name -> nx_secure_x509_org_unit = tlv_data;
269         name -> nx_secure_x509_org_unit_length = (USHORT)tlv_length;
270         break;
271 #ifdef NX_SECURE_X509_USE_EXTENDED_DISTINGUISHED_NAMES
272     /* When using extended distinguished names, parse them here. */
273     case NX_SECURE_TLS_X509_TYPE_LOCALITY:
274         name -> nx_secure_x509_locality = tlv_data;
275         name -> nx_secure_x509_locality_length = (USHORT)tlv_length;
276         break;
277 #endif
278     default:
279         /* In the case of unrecognized OIDs, just return success. There are numerous
280            extensions and their presence is not a problem if we don't recognize them. */
281         break;
282     }
283 
284     /* We have successfully extracted our data based on the OID. */
285     return(NX_SECURE_X509_SUCCESS);
286 }
287 
288