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