/* Provenance: openssl/openssl ASN1_mbstring_ncopy (fixed). * repo: openssl/openssl * commit: 97f6b621f7af2c4c4e162c75045a6e12a8fdf8d9 * parent: cb005ceaaf1fb50722e0101762a3e266338c5bf9 * commit_url: https://github.com/openssl/openssl/commit/97f6b621f7af2c4c4e162c75045a6e12a8fdf8d9 * cve: CVE-2026-7383 * license: Apache-2.0 * function: ASN1_mbstring_ncopy * relpath: crypto/asn1/a_mbstr.c */ int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len, int inform, unsigned long mask_in, long minsize, long maxsize) { int str_type; int ret; char free_out; int outform, outlen = 0; ASN1_STRING *dest; unsigned char *p; int nchar; uint32_t mask = (uint32_t)mask_in; int (*cpyfunc)(uint32_t, void *) = NULL; if (len == -1) { size_t len_s = strlen((const char *)in); if (len_s >= INT_MAX) { ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG); return -1; } len = (int)len_s; } if (!mask) mask = DIRSTRING_TYPE; if (len < 0) { ERR_raise(ERR_LIB_ASN1, ERR_R_PASSED_INVALID_ARGUMENT); return -1; } /* First do a string check and work out the number of characters */ switch (inform) { case MBSTRING_BMP: if (len & 1) { ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_BMPSTRING_LENGTH); return -1; } nchar = len >> 1; break; case MBSTRING_UNIV: if (len & 3) { ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UNIVERSALSTRING_LENGTH); return -1; } nchar = len >> 2; break; case MBSTRING_UTF8: nchar = 0; /* This counts the characters and does utf8 syntax checking */ ret = traverse_string(in, len, MBSTRING_UTF8, in_utf8, &nchar); if (ret < 0) { ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING); return -1; } break; case MBSTRING_ASC: nchar = len; break; default: ERR_raise(ERR_LIB_ASN1, ASN1_R_UNKNOWN_FORMAT); return -1; } if ((minsize > 0) && (nchar < minsize)) { ERR_raise_data(ERR_LIB_ASN1, ASN1_R_STRING_TOO_SHORT, "minsize=%ld", minsize); return -1; } if ((maxsize > 0) && (nchar > maxsize)) { ERR_raise_data(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG, "maxsize=%ld", maxsize); return -1; } /* Now work out minimal type (if any) */ if (traverse_string(in, len, inform, type_str, &mask) < 0) { ERR_raise(ERR_LIB_ASN1, ASN1_R_ILLEGAL_CHARACTERS); return -1; } /* * Now work out output format and string type. * These checks should be in sync with the checks in type_str. */ outform = MBSTRING_ASC; if (mask & B_ASN1_NUMERICSTRING) str_type = V_ASN1_NUMERICSTRING; else if (mask & B_ASN1_PRINTABLESTRING) str_type = V_ASN1_PRINTABLESTRING; else if (mask & B_ASN1_IA5STRING) str_type = V_ASN1_IA5STRING; else if (mask & B_ASN1_T61STRING) str_type = V_ASN1_T61STRING; else if (mask & B_ASN1_BMPSTRING) { str_type = V_ASN1_BMPSTRING; outform = MBSTRING_BMP; } else if (mask & B_ASN1_UNIVERSALSTRING) { str_type = V_ASN1_UNIVERSALSTRING; outform = MBSTRING_UNIV; } else { str_type = V_ASN1_UTF8STRING; outform = MBSTRING_UTF8; } if (!out) return str_type; if (*out) { free_out = 0; dest = *out; ASN1_STRING_set0(dest, NULL, 0); dest->type = str_type; } else { free_out = 1; dest = ASN1_STRING_type_new(str_type); if (dest == NULL) { ERR_raise(ERR_LIB_ASN1, ERR_R_ASN1_LIB); return -1; } *out = dest; } /* If both the same type just copy across */ if (inform == outform) { if (!ASN1_STRING_set(dest, in, len)) { if (free_out) { ASN1_STRING_free(dest); *out = NULL; } ERR_raise(ERR_LIB_ASN1, ERR_R_ASN1_LIB); return -1; } return str_type; } /* Work out how much space the destination will need */ switch (outform) { case MBSTRING_ASC: outlen = nchar; cpyfunc = cpy_asc; break; case MBSTRING_BMP: if (nchar > INT_MAX / 2) { ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG); if (free_out) { ASN1_STRING_free(dest); *out = NULL; } return -1; } outlen = nchar << 1; cpyfunc = cpy_bmp; break; case MBSTRING_UNIV: if (nchar > INT_MAX / 4) { ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG); if (free_out) { ASN1_STRING_free(dest); *out = NULL; } return -1; } outlen = nchar << 2; cpyfunc = cpy_univ; break; case MBSTRING_UTF8: outlen = 0; ret = traverse_string(in, len, inform, out_utf8, &outlen); if (ret < 0) { /* error already raised in out_utf8() */ if (free_out) { ASN1_STRING_free(dest); *out = NULL; } return -1; } cpyfunc = cpy_utf8; break; } if ((p = OPENSSL_malloc(outlen + 1)) == NULL) { if (free_out) { ASN1_STRING_free(dest); *out = NULL; } return -1; } dest->length = outlen; dest->data = p; p[outlen] = 0; traverse_string(in, len, inform, cpyfunc, &p); return str_type; }