1 /*
2  * Copyright 2007-2023 The OpenSSL Project Authors. All Rights Reserved.
3  * Copyright Nokia 2007-2019
4  * Copyright Siemens AG 2015-2019
5  *
6  * Licensed under the Apache License 2.0 (the "License").  You may not use
7  * this file except in compliance with the License.  You can obtain a copy
8  * in the file LICENSE in the source distribution or at
9  * https://www.openssl.org/source/license.html
10  */
11 
12 /* CMP functions for PKIStatusInfo handling and PKIMessage decomposition */
13 
14 #include "cmp_local.h"
15 
16 /* CMP functions related to PKIStatus */
17 
ossl_cmp_pkisi_get_status(const OSSL_CMP_PKISI * si)18 int ossl_cmp_pkisi_get_status(const OSSL_CMP_PKISI *si)
19 {
20     int res ;
21 
22     if (!ossl_assert(si != NULL && si->status != NULL))
23         return -1;
24     res = ossl_cmp_asn1_get_int(si->status);
25     return res == -2 ? -1 : res;
26 }
27 
ossl_cmp_PKIStatus_to_string(int status)28 const char *ossl_cmp_PKIStatus_to_string(int status)
29 {
30     switch (status) {
31     case OSSL_CMP_PKISTATUS_accepted:
32         return "PKIStatus: accepted";
33     case OSSL_CMP_PKISTATUS_grantedWithMods:
34         return "PKIStatus: granted with modifications";
35     case OSSL_CMP_PKISTATUS_rejection:
36         return "PKIStatus: rejection";
37     case OSSL_CMP_PKISTATUS_waiting:
38         return "PKIStatus: waiting";
39     case OSSL_CMP_PKISTATUS_revocationWarning:
40         return "PKIStatus: revocation warning - a revocation of the cert is imminent";
41     case OSSL_CMP_PKISTATUS_revocationNotification:
42         return "PKIStatus: revocation notification - a revocation of the cert has occurred";
43     case OSSL_CMP_PKISTATUS_keyUpdateWarning:
44         return "PKIStatus: key update warning - update already done for the cert";
45     default:
46         ERR_raise_data(ERR_LIB_CMP, CMP_R_ERROR_PARSING_PKISTATUS,
47                        "PKIStatus: invalid=%d", status);
48         return NULL;
49     }
50 }
51 
ossl_cmp_pkisi_get0_statusString(const OSSL_CMP_PKISI * si)52 OSSL_CMP_PKIFREETEXT *ossl_cmp_pkisi_get0_statusString(const OSSL_CMP_PKISI *si)
53 {
54     if (!ossl_assert(si != NULL))
55         return NULL;
56     return si->statusString;
57 }
58 
ossl_cmp_pkisi_get_pkifailureinfo(const OSSL_CMP_PKISI * si)59 int ossl_cmp_pkisi_get_pkifailureinfo(const OSSL_CMP_PKISI *si)
60 {
61     int i;
62     int res = 0;
63 
64     if (!ossl_assert(si != NULL))
65         return -1;
66     if (si->failInfo != NULL)
67         for (i = 0; i <= OSSL_CMP_PKIFAILUREINFO_MAX; i++)
68             if (ASN1_BIT_STRING_get_bit(si->failInfo, i))
69                 res |= 1 << i;
70     return res;
71 }
72 
73 /*-
74  * convert PKIFailureInfo number to human-readable string
75  * returns pointer to static string, or NULL on error
76  */
CMP_PKIFAILUREINFO_to_string(int number)77 static const char *CMP_PKIFAILUREINFO_to_string(int number)
78 {
79     switch (number) {
80     case OSSL_CMP_PKIFAILUREINFO_badAlg:
81         return "badAlg";
82     case OSSL_CMP_PKIFAILUREINFO_badMessageCheck:
83         return "badMessageCheck";
84     case OSSL_CMP_PKIFAILUREINFO_badRequest:
85         return "badRequest";
86     case OSSL_CMP_PKIFAILUREINFO_badTime:
87         return "badTime";
88     case OSSL_CMP_PKIFAILUREINFO_badCertId:
89         return "badCertId";
90     case OSSL_CMP_PKIFAILUREINFO_badDataFormat:
91         return "badDataFormat";
92     case OSSL_CMP_PKIFAILUREINFO_wrongAuthority:
93         return "wrongAuthority";
94     case OSSL_CMP_PKIFAILUREINFO_incorrectData:
95         return "incorrectData";
96     case OSSL_CMP_PKIFAILUREINFO_missingTimeStamp:
97         return "missingTimeStamp";
98     case OSSL_CMP_PKIFAILUREINFO_badPOP:
99         return "badPOP";
100     case OSSL_CMP_PKIFAILUREINFO_certRevoked:
101         return "certRevoked";
102     case OSSL_CMP_PKIFAILUREINFO_certConfirmed:
103         return "certConfirmed";
104     case OSSL_CMP_PKIFAILUREINFO_wrongIntegrity:
105         return "wrongIntegrity";
106     case OSSL_CMP_PKIFAILUREINFO_badRecipientNonce:
107         return "badRecipientNonce";
108     case OSSL_CMP_PKIFAILUREINFO_timeNotAvailable:
109         return "timeNotAvailable";
110     case OSSL_CMP_PKIFAILUREINFO_unacceptedPolicy:
111         return "unacceptedPolicy";
112     case OSSL_CMP_PKIFAILUREINFO_unacceptedExtension:
113         return "unacceptedExtension";
114     case OSSL_CMP_PKIFAILUREINFO_addInfoNotAvailable:
115         return "addInfoNotAvailable";
116     case OSSL_CMP_PKIFAILUREINFO_badSenderNonce:
117         return "badSenderNonce";
118     case OSSL_CMP_PKIFAILUREINFO_badCertTemplate:
119         return "badCertTemplate";
120     case OSSL_CMP_PKIFAILUREINFO_signerNotTrusted:
121         return "signerNotTrusted";
122     case OSSL_CMP_PKIFAILUREINFO_transactionIdInUse:
123         return "transactionIdInUse";
124     case OSSL_CMP_PKIFAILUREINFO_unsupportedVersion:
125         return "unsupportedVersion";
126     case OSSL_CMP_PKIFAILUREINFO_notAuthorized:
127         return "notAuthorized";
128     case OSSL_CMP_PKIFAILUREINFO_systemUnavail:
129         return "systemUnavail";
130     case OSSL_CMP_PKIFAILUREINFO_systemFailure:
131         return "systemFailure";
132     case OSSL_CMP_PKIFAILUREINFO_duplicateCertReq:
133         return "duplicateCertReq";
134     default:
135         return NULL; /* illegal failure number */
136     }
137 }
138 
ossl_cmp_pkisi_check_pkifailureinfo(const OSSL_CMP_PKISI * si,int bit_index)139 int ossl_cmp_pkisi_check_pkifailureinfo(const OSSL_CMP_PKISI *si, int bit_index)
140 {
141     if (!ossl_assert(si != NULL && si->failInfo != NULL))
142         return -1;
143     if (bit_index < 0 || bit_index > OSSL_CMP_PKIFAILUREINFO_MAX) {
144         ERR_raise(ERR_LIB_CMP, CMP_R_INVALID_ARGS);
145         return -1;
146     }
147 
148     return ASN1_BIT_STRING_get_bit(si->failInfo, bit_index);
149 }
150 
151 /*-
152  * place human-readable error string created from PKIStatusInfo in given buffer
153  * returns pointer to the same buffer containing the string, or NULL on error
154  */
155 static
snprint_PKIStatusInfo_parts(int status,int fail_info,const OSSL_CMP_PKIFREETEXT * status_strings,char * buf,size_t bufsize)156 char *snprint_PKIStatusInfo_parts(int status, int fail_info,
157                                   const OSSL_CMP_PKIFREETEXT *status_strings,
158                                   char *buf, size_t bufsize)
159 {
160     int failure;
161     const char *status_string, *failure_string;
162     ASN1_UTF8STRING *text;
163     int i;
164     int printed_chars;
165     int failinfo_found = 0;
166     int n_status_strings;
167     char *write_ptr = buf;
168 
169     if (buf == NULL
170             || status < 0
171             || (status_string = ossl_cmp_PKIStatus_to_string(status)) == NULL)
172         return NULL;
173 
174 #define ADVANCE_BUFFER  \
175     if (printed_chars < 0 || (size_t)printed_chars >= bufsize)  \
176         return NULL; \
177     write_ptr += printed_chars; \
178     bufsize -= printed_chars;
179 
180     printed_chars = BIO_snprintf(write_ptr, bufsize, "%s", status_string);
181     ADVANCE_BUFFER;
182 
183     /*
184      * failInfo is optional and may be empty;
185      * if present, print failInfo before statusString because it is more concise
186      */
187     if (fail_info != -1 && fail_info != 0) {
188         printed_chars = BIO_snprintf(write_ptr, bufsize, "; PKIFailureInfo: ");
189         ADVANCE_BUFFER;
190         for (failure = 0; failure <= OSSL_CMP_PKIFAILUREINFO_MAX; failure++) {
191             if ((fail_info & (1 << failure)) != 0) {
192                 failure_string = CMP_PKIFAILUREINFO_to_string(failure);
193                 if (failure_string != NULL) {
194                     printed_chars = BIO_snprintf(write_ptr, bufsize, "%s%s",
195                                                  failinfo_found ? ", " : "",
196                                                  failure_string);
197                     ADVANCE_BUFFER;
198                     failinfo_found = 1;
199                 }
200             }
201         }
202     }
203     if (!failinfo_found && status != OSSL_CMP_PKISTATUS_accepted
204             && status != OSSL_CMP_PKISTATUS_grantedWithMods) {
205         printed_chars = BIO_snprintf(write_ptr, bufsize, "; <no failure info>");
206         ADVANCE_BUFFER;
207     }
208 
209     /* statusString sequence is optional and may be empty */
210     n_status_strings = sk_ASN1_UTF8STRING_num(status_strings);
211     if (n_status_strings > 0) {
212         printed_chars = BIO_snprintf(write_ptr, bufsize, "; StatusString%s: ",
213                                      n_status_strings > 1 ? "s" : "");
214         ADVANCE_BUFFER;
215         for (i = 0; i < n_status_strings; i++) {
216             text = sk_ASN1_UTF8STRING_value(status_strings, i);
217             printed_chars = BIO_snprintf(write_ptr, bufsize, "\"%.*s\"%s",
218                                          ASN1_STRING_length(text),
219                                          ASN1_STRING_get0_data(text),
220                                          i < n_status_strings - 1 ? ", " : "");
221             ADVANCE_BUFFER;
222         }
223     }
224 #undef ADVANCE_BUFFER
225     return buf;
226 }
227 
OSSL_CMP_snprint_PKIStatusInfo(const OSSL_CMP_PKISI * statusInfo,char * buf,size_t bufsize)228 char *OSSL_CMP_snprint_PKIStatusInfo(const OSSL_CMP_PKISI *statusInfo,
229                                      char *buf, size_t bufsize)
230 {
231     int failure_info;
232 
233     if (statusInfo == NULL) {
234         ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT);
235         return NULL;
236     }
237 
238     failure_info = ossl_cmp_pkisi_get_pkifailureinfo(statusInfo);
239 
240     return snprint_PKIStatusInfo_parts(ASN1_INTEGER_get(statusInfo->status),
241                                        failure_info,
242                                        statusInfo->statusString, buf, bufsize);
243 }
244 
OSSL_CMP_CTX_snprint_PKIStatus(const OSSL_CMP_CTX * ctx,char * buf,size_t bufsize)245 char *OSSL_CMP_CTX_snprint_PKIStatus(const OSSL_CMP_CTX *ctx, char *buf,
246                                      size_t bufsize)
247 {
248     if (ctx == NULL) {
249         ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT);
250         return NULL;
251     }
252 
253     return snprint_PKIStatusInfo_parts(OSSL_CMP_CTX_get_status(ctx),
254                                        OSSL_CMP_CTX_get_failInfoCode(ctx),
255                                        OSSL_CMP_CTX_get0_statusString(ctx),
256                                        buf, bufsize);
257 }
258 
259 /*-
260  * Creates a new PKIStatusInfo structure and fills it in
261  * returns a pointer to the structure on success, NULL on error
262  * note: strongly overlaps with TS_RESP_CTX_set_status_info()
263  * and TS_RESP_CTX_add_failure_info() in ../ts/ts_rsp_sign.c
264  */
OSSL_CMP_STATUSINFO_new(int status,int fail_info,const char * text)265 OSSL_CMP_PKISI *OSSL_CMP_STATUSINFO_new(int status, int fail_info,
266                                         const char *text)
267 {
268     OSSL_CMP_PKISI *si = OSSL_CMP_PKISI_new();
269     ASN1_UTF8STRING *utf8_text = NULL;
270     int failure;
271 
272     if (si == NULL)
273         goto err;
274     if (!ASN1_INTEGER_set(si->status, status))
275         goto err;
276 
277     if (text != NULL) {
278         if ((utf8_text = ASN1_UTF8STRING_new()) == NULL
279                 || !ASN1_STRING_set(utf8_text, text, -1))
280             goto err;
281         if ((si->statusString = sk_ASN1_UTF8STRING_new_null()) == NULL)
282             goto err;
283         if (!sk_ASN1_UTF8STRING_push(si->statusString, utf8_text))
284             goto err;
285         /* Ownership is lost. */
286         utf8_text = NULL;
287     }
288 
289     for (failure = 0; failure <= OSSL_CMP_PKIFAILUREINFO_MAX; failure++) {
290         if ((fail_info & (1 << failure)) != 0) {
291             if (si->failInfo == NULL
292                     && (si->failInfo = ASN1_BIT_STRING_new()) == NULL)
293                 goto err;
294             if (!ASN1_BIT_STRING_set_bit(si->failInfo, failure, 1))
295                 goto err;
296         }
297     }
298     return si;
299 
300  err:
301     OSSL_CMP_PKISI_free(si);
302     ASN1_UTF8STRING_free(utf8_text);
303     return NULL;
304 }
305