/* Provenance: openssl/openssl gid_cb (fixed). * repo: openssl/openssl * commit: 85977e013f32ceb96aa034c0e741adddc1a05e34 * parent: 21d525dc846aed171fc886f3da614a6645948f13 * commit_url: https://github.com/openssl/openssl/commit/85977e013f32ceb96aa034c0e741adddc1a05e34 * cve: CVE-2026-2673 * license: Apache-2.0 * function: gid_cb * relpath: ssl/t1_lib.c */ static int gid_cb(const char *elem, int len, void *arg) { gid_cb_st *garg = arg; size_t i, j, k; uint16_t gid = 0; int found_group = 0; char etmp[GROUP_NAME_BUFFER_LENGTH]; int retval = 1; /* We assume success */ const char *current_prefix; int ignore_unknown = 0; int add_keyshare = 0; int remove_group = 0; size_t restored_prefix_index = 0; char *restored_default_group_string; int continue_while_loop = 1; /* Sanity checks */ if (garg == NULL || elem == NULL || len <= 0) { ERR_raise(ERR_LIB_SSL, SSL_R_UNSUPPORTED_CONFIG_VALUE); return 0; } /* Check the possible prefixes (remark: Leading and trailing spaces already cleared) */ while (continue_while_loop && len > 0 && ((current_prefix = strchr(prefixes, elem[0])) != NULL || OPENSSL_strncasecmp(current_prefix = (char *)DEFAULT_GROUPNAME_FIRST_CHARACTER, elem, 1) == 0)) { switch (*current_prefix) { case TUPLE_DELIMITER_CHARACTER: /* tuple delimiter not allowed here -> syntax error */ return -1; break; case GROUP_DELIMITER_CHARACTER: return -1; /* Not a valid prefix for a single group name-> syntax error */ break; case KEY_SHARE_INDICATOR_CHARACTER: if (add_keyshare) return -1; /* Only single key share prefix allowed -> syntax error */ add_keyshare = 1; ++elem; --len; break; case REMOVE_GROUP_INDICATOR_CHARACTER: if (remove_group) return -1; /* Only single remove group prefix allowed -> syntax error */ remove_group = 1; ++elem; --len; break; case IGNORE_UNKNOWN_GROUP_CHARACTER: if (ignore_unknown) return -1; /* Only single ? allowed -> syntax error */ ignore_unknown = 1; ++elem; --len; break; default: /* * Check whether a DEFAULT[_XYZ] 'pseudo group' (= a built-in * list of groups) should be added */ for (i = 0; i < OSSL_NELEM(default_group_strings); i++) { if ((size_t)len == (strlen(default_group_strings[i].list_name)) && OPENSSL_strncasecmp(default_group_strings[i].list_name, elem, len) == 0) { int saved_first; /* * We're asked to insert an entire list of groups from a * DEFAULT[_XYZ] 'pseudo group' which we do by * recursively calling this function (indirectly via * CONF_parse_list and tuple_cb); essentially, we treat a DEFAULT * group string like a tuple which is appended to the current tuple * rather then starting a new tuple. */ if (ignore_unknown || remove_group) return -1; /* removal or ignore not allowed here -> syntax error */ /* * First, we restore any keyshare prefix in a new zero-terminated string * (if not already present) */ restored_default_group_string = OPENSSL_malloc((1 /* max prefix length */ + strlen(default_group_strings[i].group_string) + 1 /* \0 */) * sizeof(char)); if (restored_default_group_string == NULL) return 0; if (add_keyshare /* Remark: we tolerate a duplicated keyshare indicator here */ && default_group_strings[i].group_string[0] != KEY_SHARE_INDICATOR_CHARACTER) restored_default_group_string[restored_prefix_index++] = KEY_SHARE_INDICATOR_CHARACTER; memcpy(restored_default_group_string + restored_prefix_index, default_group_strings[i].group_string, strlen(default_group_strings[i].group_string)); restored_default_group_string[strlen(default_group_strings[i].group_string) + restored_prefix_index] = '\0'; /* * Append first tuple of result to current tuple, and don't * terminate the last tuple until we return to a top-level * tuple_cb. */ saved_first = garg->first; garg->inner = garg->first = 1; retval = CONF_parse_list(restored_default_group_string, TUPLE_DELIMITER_CHARACTER, 1, tuple_cb, garg); garg->inner = 0; garg->first = saved_first; /* We don't need the \0-terminated string anymore */ OPENSSL_free(restored_default_group_string); return retval; } } /* * If we reached this point, a group name started with a 'd' or 'D', but no request * for a DEFAULT[_XYZ] 'pseudo group' was detected, hence processing of the group * name can continue as usual (= the while loop checking prefixes can end) */ continue_while_loop = 0; break; } } if (len == 0) return -1; /* Seems we have prefxes without a group name -> syntax error */ /* Memory management in case more groups are present compared to initial allocation */ if (garg->gidcnt == garg->gidmax) { uint16_t *tmp = OPENSSL_realloc(garg->gid_arr, (garg->gidmax + GROUPLIST_INCREMENT) * sizeof(*garg->gid_arr)); if (tmp == NULL) return 0; garg->gidmax += GROUPLIST_INCREMENT; garg->gid_arr = tmp; } /* Memory management for key share groups */ if (garg->ksidcnt == garg->ksidmax) { uint16_t *tmp = OPENSSL_realloc(garg->ksid_arr, (garg->ksidmax + GROUPLIST_INCREMENT) * sizeof(*garg->ksid_arr)); if (tmp == NULL) return 0; garg->ksidmax += GROUPLIST_INCREMENT; garg->ksid_arr = tmp; } if (len > (int)(sizeof(etmp) - 1)) return -1; /* group name to long -> syntax error */ /* * Prepare addition or removal of a single group by converting * a group name into its groupID equivalent */ /* Create a \0-terminated string and get the gid for this group if possible */ memcpy(etmp, elem, len); etmp[len] = 0; /* Get the groupID */ gid = tls1_group_name2id(garg->ctx, etmp); /* * Handle the case where no valid groupID was returned * e.g. for an unknown group, which we'd ignore (only) if relevant prefix was set */ if (gid == 0) { /* Is it one of the GOST groups ? */ for (i = 0; i < OSSL_NELEM(name2id_arr); i++) { if (OPENSSL_strcasecmp(etmp, name2id_arr[i].group_name) == 0) { gid = name2id_arr[i].groupID; break; } } if (gid == 0) { /* still not found */ /* Unknown group - ignore if ignore_unknown; trigger error otherwise */ retval = ignore_unknown; goto done; } } /* Make sure that at least one provider is supporting this groupID */ found_group = 0; for (j = 0; j < garg->ctx->group_list_len; j++) if (garg->ctx->group_list[j].group_id == gid) { found_group = 1; break; } /* * No provider supports this group - ignore if * ignore_unknown; trigger error otherwise */ if (found_group == 0) { retval = ignore_unknown; goto done; } /* Remove group (and keyshare) from anywhere in the list if present, ignore if not present */ if (remove_group) { /* Is the current group specified anywhere in the entire list so far? */ found_group = 0; for (i = 0; i < garg->gidcnt; i++) if (garg->gid_arr[i] == gid) { found_group = 1; break; } /* The group to remove is at position i in the list of (zero indexed) groups */ if (found_group) { /* We remove that group from its position (which is at i)... */ for (j = i; j < (garg->gidcnt - 1); j++) garg->gid_arr[j] = garg->gid_arr[j + 1]; /* ...shift remaining groups left ... */ garg->gidcnt--; /* ..and update the book keeping for the number of groups */ /* * We also must update the number of groups either in a previous tuple (which we * must identify and check whether it becomes empty due to the deletion) or in * the current tuple, pending where the deleted group resides */ k = 0; for (j = 0; j < garg->tplcnt; j++) { k += garg->tuplcnt_arr[j]; /* Remark: i is zero-indexed, k is one-indexed */ if (k > i) { /* remove from one of the previous tuples */ garg->tuplcnt_arr[j]--; break; /* We took care not to have group duplicates, hence we can stop here */ } } if (k <= i) /* remove from current tuple */ garg->tuplcnt_arr[j]--; /* We also remove the group from the list of keyshares (if present) */ found_group = 0; for (i = 0; i < garg->ksidcnt; i++) if (garg->ksid_arr[i] == gid) { found_group = 1; break; } if (found_group) { /* Found, hence we remove that keyshare from its position (which is at i)... */ for (j = i; j < (garg->ksidcnt - 1); j++) garg->ksid_arr[j] = garg->ksid_arr[j + 1]; /* shift remaining key shares */ /* ... and update the book keeping */ garg->ksidcnt--; } } } else { /* Processing addition of a single new group */ /* Check for duplicates */ for (i = 0; i < garg->gidcnt; i++) if (garg->gid_arr[i] == gid) { /* Duplicate group anywhere in the list of groups - ignore */ goto done; } /* Add the current group to the 'flat' list of groups */ garg->gid_arr[garg->gidcnt++] = gid; /* and update the book keeping for the number of groups in current tuple */ garg->tuplcnt_arr[garg->tplcnt]++; /* We want to add a key share for the current group */ if (add_keyshare) garg->ksid_arr[garg->ksidcnt++] = gid; } done: return retval; }