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 
27 /**************************************************************************/
28 /*                                                                        */
29 /*  FUNCTION                                               RELEASE        */
30 /*                                                                        */
31 /*    _nx_secure_x509_subject_alt_names_find              PORTABLE C      */
32 /*                                                           6.1.6        */
33 /*  AUTHOR                                                                */
34 /*                                                                        */
35 /*    Timothy Stapko, Microsoft Corporation                               */
36 /*                                                                        */
37 /*  DESCRIPTION                                                           */
38 /*                                                                        */
39 /*    This function parses through the list of names in an X.509          */
40 /*    subjectAltName extension, looking for a particular name. This is    */
41 /*    typically used to see if a DNS name is present in a subjectAltName  */
42 /*    extension if the Common Name did not match.                         */
43 /*                                                                        */
44 /*  INPUT                                                                 */
45 /*                                                                        */
46 /*    extension                             subjectAltName extension data */
47 /*    name                                  Name to search for            */
48 /*    name_length                           Length of name                */
49 /*    name_type                             Type of name                  */
50 /*                                                                        */
51 /*  OUTPUT                                                                */
52 /*                                                                        */
53 /*    status                                Completion status             */
54 /*                                                                        */
55 /*  CALLS                                                                 */
56 /*                                                                        */
57 /*    _nx_secure_x509_asn1_tlv_block_parse  Parse ASN.1 block             */
58 /*    _nx_secure_x509_wildcard_compare      Wildcard compare for names    */
59 /*                                                                        */
60 /*  CALLED BY                                                             */
61 /*                                                                        */
62 /*    _nx_secure_x509_common_name_dns_check Check Common Name by DNS      */
63 /*                                                                        */
64 /*  RELEASE HISTORY                                                       */
65 /*                                                                        */
66 /*    DATE              NAME                      DESCRIPTION             */
67 /*                                                                        */
68 /*  05-19-2020     Timothy Stapko           Initial Version 6.0           */
69 /*  09-30-2020     Timothy Stapko           Modified comment(s),          */
70 /*                                            resulting in version 6.1    */
71 /*  04-02-2021     Timothy Stapko           Modified comment(s),          */
72 /*                                            removed dependency on TLS,  */
73 /*                                            resulting in version 6.1.6  */
74 /*                                                                        */
75 /**************************************************************************/
_nx_secure_x509_subject_alt_names_find(NX_SECURE_X509_EXTENSION * extension,const UCHAR * name,UINT name_length,USHORT name_type)76 UINT _nx_secure_x509_subject_alt_names_find(NX_SECURE_X509_EXTENSION *extension, const UCHAR *name,
77                                             UINT name_length, USHORT name_type)
78 {
79 USHORT       tlv_type;
80 USHORT       tlv_type_class;
81 ULONG        tlv_length;
82 const UCHAR *tlv_data;
83 const UCHAR *current_buffer;
84 ULONG        length;
85 ULONG        header_length;
86 UINT         status;
87 const UCHAR *compare_name;
88 ULONG        compare_length;
89 INT          compare_value;
90 
91     /* Now, parse the subjectAltName extension. */
92     /* subjectAltName ASN.1 format:
93         SubjectAltName ::= GeneralNames
94 
95         GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
96 
97         GeneralName ::= CHOICE {
98              otherName                 [0]  AnotherName,
99              rfc822Name                [1]  IA5String,
100              dNSName                   [2]  IA5String,
101              x400Address               [3]  ORAddress,
102              directoryName             [4]  Name,
103              ediPartyName              [5]  EDIPartyName,
104              uniformResourceIdentifier [6]  IA5String,
105              iPAddress                 [7]  OCTET STRING,
106              registeredID              [8]  OBJECT IDENTIFIER }
107 
108         AnotherName ::= SEQUENCE {
109              type-id    OBJECT IDENTIFIER,
110              value      [0] EXPLICIT ANY DEFINED BY type-id }
111 
112         EDIPartyName ::= SEQUENCE {
113              nameAssigner              [0]  DirectoryString OPTIONAL,
114              partyName                 [1]  DirectoryString }
115      */
116 
117     /* The length of our extensions is the length of the sequence. */
118     current_buffer = extension -> nx_secure_x509_extension_data;
119     length = extension -> nx_secure_x509_extension_data_length;
120 
121     /*  First, parse the name sequence. */
122     status = _nx_secure_x509_asn1_tlv_block_parse(current_buffer, &length, &tlv_type, &tlv_type_class, &tlv_length, &tlv_data, &header_length);
123 
124     /*  Make sure we parsed the block alright. */
125     if (status != 0)
126     {
127         return(status);
128     }
129 
130     /* If the next item up is not a sequence, then it isn't an extensions block. */
131     if (!(tlv_type_class == NX_SECURE_ASN_TAG_CLASS_UNIVERSAL && tlv_type == NX_SECURE_ASN_TAG_SEQUENCE))
132     {
133         /* The extensions sequence isn't empty and we should be seeing another extension sequence
134            but we got something else so something is amiss. */
135         return(NX_SECURE_X509_INVALID_EXTENSION_SEQUENCE);
136     }
137 
138     /* The names are in the body of the sequence structure, so use our tlv_data and length. */
139     current_buffer = tlv_data;
140     length = tlv_length;
141 
142 
143     /* Keep looping until we run out of data to parse. */
144     while (length > 0)
145     {
146         /*  First, parse the context-specific tag (if it exists). */
147         status = _nx_secure_x509_asn1_tlv_block_parse(current_buffer, &length, &tlv_type, &tlv_type_class, &tlv_length, &tlv_data, &header_length);
148 
149         /*  Make sure we parsed the block alright. */
150         if (status != 0)
151         {
152             return(status);
153         }
154 
155         /* If the next item up is not a context-sensitive tag, then not a valid subjectAltName. */
156         if (!(tlv_type_class == NX_SECURE_ASN_TAG_CLASS_CONTEXT))
157         {
158             /* No extensions block is OK because it is non-existent in v1 and v2, and
159                OPTIONAL in v3. */
160             return(NX_SECURE_X509_ALT_NAME_NOT_FOUND);
161         }
162 
163         current_buffer += header_length;
164 
165         /* If the name type we are searching for doesn't match what we just parsed,
166            continue to the next entry. */
167         if (tlv_type != name_type)
168         {
169             continue;
170         }
171 
172         /* Process the name type we found. */
173         switch (tlv_type)
174         {
175         case NX_SECURE_X509_SUB_ALT_NAME_TAG_DNSNAME:
176             /* Now we have an IA5 string to compare against our name. */
177             compare_name = tlv_data;
178             compare_length = tlv_length;
179             break;
180         case NX_SECURE_X509_SUB_ALT_NAME_TAG_OTHERNAME:
181         case NX_SECURE_X509_SUB_ALT_NAME_TAG_RFC822NAME:
182         case NX_SECURE_X509_SUB_ALT_NAME_TAG_X400ADDRESS:
183         case NX_SECURE_X509_SUB_ALT_NAME_TAG_DIRECTORYNAME:
184         case NX_SECURE_X509_SUB_ALT_NAME_TAG_EDIPARTYNAME:
185         case NX_SECURE_X509_SUB_ALT_NAME_TAG_UNIFORMRESOURCEIDENTIFIER:
186         case NX_SECURE_X509_SUB_ALT_NAME_TAG_IPADDRESS:
187         case NX_SECURE_X509_SUB_ALT_NAME_TAG_REGISTEREDID:
188         default:
189             /* Deliberate fall-through. These name types are not supported. */
190             continue;
191         }
192 
193         /* Compare the names, using wildcard matching. */
194         compare_value =  _nx_secure_x509_wildcard_compare(name, name_length,
195                                                           compare_name, compare_length);
196 
197         if (compare_value == 0)
198         {
199             /* We found a match! */
200             return(NX_SECURE_X509_SUCCESS);
201         }
202 
203         current_buffer += tlv_length;
204     } /* End while-loop. */
205 
206     return(NX_SECURE_X509_ALT_NAME_NOT_FOUND);
207 }
208 
209