1/*
2 * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
3 * Copyright (c) 2019, Oracle and/or its affiliates.  All rights reserved.
4 *
5 * Licensed under the Apache License 2.0 (the "License").  You may not use
6 * this file except in compliance with the License.  You can obtain a copy
7 * in the file LICENSE in the source distribution or at
8 * https://www.openssl.org/source/license.html
9 */
10
11#include <string.h>
12#include <stdio.h>
13#include <stdarg.h>
14#include <openssl/err.h>
15#include "internal/propertyerr.h"
16#include "internal/property.h"
17#include "crypto/ctype.h"
18#include "internal/nelem.h"
19#include "property_local.h"
20#include "e_os.h"
21
22DEFINE_STACK_OF(OSSL_PROPERTY_DEFINITION)
23
24static const char *skip_space(const char *s)
25{
26    while (ossl_isspace(*s))
27        s++;
28    return s;
29}
30
31static int match_ch(const char *t[], char m)
32{
33    const char *s = *t;
34
35    if (*s == m) {
36        *t = skip_space(s + 1);
37        return 1;
38    }
39    return 0;
40}
41
42#define MATCH(s, m) match(s, m, sizeof(m) - 1)
43
44static int match(const char *t[], const char m[], size_t m_len)
45{
46    const char *s = *t;
47
48    if (OPENSSL_strncasecmp(s, m, m_len) == 0) {
49        *t = skip_space(s + m_len);
50        return 1;
51    }
52    return 0;
53}
54
55static int parse_name(OSSL_LIB_CTX *ctx, const char *t[], int create,
56                      OSSL_PROPERTY_IDX *idx)
57{
58    char name[100];
59    int err = 0;
60    size_t i = 0;
61    const char *s = *t;
62    int user_name = 0;
63
64    for (;;) {
65        if (!ossl_isalpha(*s)) {
66            ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_IDENTIFIER,
67                           "HERE-->%s", *t);
68            return 0;
69        }
70        do {
71            if (i < sizeof(name) - 1)
72                name[i++] = ossl_tolower(*s);
73            else
74                err = 1;
75        } while (*++s == '_' || ossl_isalnum(*s));
76        if (*s != '.')
77            break;
78        user_name = 1;
79        if (i < sizeof(name) - 1)
80            name[i++] = *s;
81        else
82            err = 1;
83        s++;
84    }
85    name[i] = '\0';
86    if (err) {
87        ERR_raise_data(ERR_LIB_PROP, PROP_R_NAME_TOO_LONG, "HERE-->%s", *t);
88        return 0;
89    }
90    *t = skip_space(s);
91    *idx = ossl_property_name(ctx, name, user_name && create);
92    return 1;
93}
94
95static int parse_number(const char *t[], OSSL_PROPERTY_DEFINITION *res)
96{
97    const char *s = *t;
98    int64_t v = 0;
99
100    do {
101        if (!ossl_isdigit(*s)) {
102            ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_A_DECIMAL_DIGIT,
103                           "HERE-->%s", *t);
104            return 0;
105        }
106        /* overflow check */
107        if (v > ((INT64_MAX - (*s - '0')) / 10)) {
108            ERR_raise_data(ERR_LIB_PROP, PROP_R_PARSE_FAILED,
109                           "Property %s overflows", *t);
110            return 0;
111        }
112        v = v * 10 + (*s++ - '0');
113    } while (ossl_isdigit(*s));
114    if (!ossl_isspace(*s) && *s != '\0' && *s != ',') {
115        ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_A_DECIMAL_DIGIT,
116                       "HERE-->%s", *t);
117        return 0;
118    }
119    *t = skip_space(s);
120    res->type = OSSL_PROPERTY_TYPE_NUMBER;
121    res->v.int_val = v;
122    return 1;
123}
124
125static int parse_hex(const char *t[], OSSL_PROPERTY_DEFINITION *res)
126{
127    const char *s = *t;
128    int64_t v = 0;
129    int sval;
130
131    do {
132        if (ossl_isdigit(*s)) {
133            sval = *s - '0';
134        } else if (ossl_isxdigit(*s)) {
135            sval = ossl_tolower(*s) - 'a' + 10;
136        } else {
137            ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_HEXADECIMAL_DIGIT,
138                           "%s", *t);
139            return 0;
140        }
141
142        if (v > ((INT64_MAX - sval) / 16)) {
143            ERR_raise_data(ERR_LIB_PROP, PROP_R_PARSE_FAILED,
144                           "Property %s overflows", *t);
145            return 0;
146        }
147
148        v <<= 4;
149        v += sval;
150    } while (ossl_isxdigit(*++s));
151    if (!ossl_isspace(*s) && *s != '\0' && *s != ',') {
152        ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_HEXADECIMAL_DIGIT,
153                       "HERE-->%s", *t);
154        return 0;
155    }
156    *t = skip_space(s);
157    res->type = OSSL_PROPERTY_TYPE_NUMBER;
158    res->v.int_val = v;
159    return 1;
160}
161
162static int parse_oct(const char *t[], OSSL_PROPERTY_DEFINITION *res)
163{
164    const char *s = *t;
165    int64_t v = 0;
166
167    do {
168        if (*s == '9' || *s == '8' || !ossl_isdigit(*s)) {
169            ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_OCTAL_DIGIT,
170                           "HERE-->%s", *t);
171            return 0;
172        }
173        if (v > ((INT64_MAX - (*s - '0')) / 8)) {
174            ERR_raise_data(ERR_LIB_PROP, PROP_R_PARSE_FAILED,
175                           "Property %s overflows", *t);
176            return 0;
177        }
178
179        v = (v << 3) + (*s - '0');
180    } while (ossl_isdigit(*++s) && *s != '9' && *s != '8');
181    if (!ossl_isspace(*s) && *s != '\0' && *s != ',') {
182        ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_OCTAL_DIGIT,
183                       "HERE-->%s", *t);
184        return 0;
185    }
186    *t = skip_space(s);
187    res->type = OSSL_PROPERTY_TYPE_NUMBER;
188    res->v.int_val = v;
189    return 1;
190}
191
192static int parse_string(OSSL_LIB_CTX *ctx, const char *t[], char delim,
193                        OSSL_PROPERTY_DEFINITION *res, const int create)
194{
195    char v[1000];
196    const char *s = *t;
197    size_t i = 0;
198    int err = 0;
199
200    while (*s != '\0' && *s != delim) {
201        if (i < sizeof(v) - 1)
202            v[i++] = *s;
203        else
204            err = 1;
205        s++;
206    }
207    if (*s == '\0') {
208        ERR_raise_data(ERR_LIB_PROP, PROP_R_NO_MATCHING_STRING_DELIMITER,
209                       "HERE-->%c%s", delim, *t);
210        return 0;
211    }
212    v[i] = '\0';
213    if (err) {
214        ERR_raise_data(ERR_LIB_PROP, PROP_R_STRING_TOO_LONG, "HERE-->%s", *t);
215    } else {
216        res->v.str_val = ossl_property_value(ctx, v, create);
217    }
218    *t = skip_space(s + 1);
219    res->type = OSSL_PROPERTY_TYPE_STRING;
220    return !err;
221}
222
223static int parse_unquoted(OSSL_LIB_CTX *ctx, const char *t[],
224                          OSSL_PROPERTY_DEFINITION *res, const int create)
225{
226    char v[1000];
227    const char *s = *t;
228    size_t i = 0;
229    int err = 0;
230
231    if (*s == '\0' || *s == ',')
232        return 0;
233    while (ossl_isprint(*s) && !ossl_isspace(*s) && *s != ',') {
234        if (i < sizeof(v) - 1)
235            v[i++] = ossl_tolower(*s);
236        else
237            err = 1;
238        s++;
239    }
240    if (!ossl_isspace(*s) && *s != '\0' && *s != ',') {
241        ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_ASCII_CHARACTER,
242                       "HERE-->%s", s);
243        return 0;
244    }
245    v[i] = 0;
246    if (err)
247        ERR_raise_data(ERR_LIB_PROP, PROP_R_STRING_TOO_LONG, "HERE-->%s", *t);
248    else if ((res->v.str_val = ossl_property_value(ctx, v, create)) == 0)
249        err = 1;
250    *t = skip_space(s);
251    res->type = OSSL_PROPERTY_TYPE_STRING;
252    return !err;
253}
254
255static int parse_value(OSSL_LIB_CTX *ctx, const char *t[],
256                       OSSL_PROPERTY_DEFINITION *res, int create)
257{
258    const char *s = *t;
259    int r = 0;
260
261    if (*s == '"' || *s == '\'') {
262        s++;
263        r = parse_string(ctx, &s, s[-1], res, create);
264    } else if (*s == '+') {
265        s++;
266        r = parse_number(&s, res);
267    } else if (*s == '-') {
268        s++;
269        r = parse_number(&s, res);
270        res->v.int_val = -res->v.int_val;
271    } else if (*s == '0' && s[1] == 'x') {
272        s += 2;
273        r = parse_hex(&s, res);
274    } else if (*s == '0' && ossl_isdigit(s[1])) {
275        s++;
276        r = parse_oct(&s, res);
277    } else if (ossl_isdigit(*s)) {
278        return parse_number(t, res);
279    } else if (ossl_isalpha(*s))
280        return parse_unquoted(ctx, t, res, create);
281    if (r)
282        *t = s;
283    return r;
284}
285
286static int pd_compare(const OSSL_PROPERTY_DEFINITION *const *p1,
287                      const OSSL_PROPERTY_DEFINITION *const *p2)
288{
289    const OSSL_PROPERTY_DEFINITION *pd1 = *p1;
290    const OSSL_PROPERTY_DEFINITION *pd2 = *p2;
291
292    if (pd1->name_idx < pd2->name_idx)
293        return -1;
294    if (pd1->name_idx > pd2->name_idx)
295        return 1;
296    return 0;
297}
298
299static void pd_free(OSSL_PROPERTY_DEFINITION *pd)
300{
301    OPENSSL_free(pd);
302}
303
304/*
305 * Convert a stack of property definitions and queries into a fixed array.
306 * The items are sorted for efficient query.  The stack is not freed.
307 * This function also checks for duplicated names and returns an error if
308 * any exist.
309 */
310static OSSL_PROPERTY_LIST *
311stack_to_property_list(OSSL_LIB_CTX *ctx,
312                       STACK_OF(OSSL_PROPERTY_DEFINITION) *sk)
313{
314    const int n = sk_OSSL_PROPERTY_DEFINITION_num(sk);
315    OSSL_PROPERTY_LIST *r;
316    OSSL_PROPERTY_IDX prev_name_idx = 0;
317    int i;
318
319    r = OPENSSL_malloc(sizeof(*r)
320                       + (n <= 0 ? 0 : n - 1) * sizeof(r->properties[0]));
321    if (r != NULL) {
322        sk_OSSL_PROPERTY_DEFINITION_sort(sk);
323
324        r->has_optional = 0;
325        for (i = 0; i < n; i++) {
326            r->properties[i] = *sk_OSSL_PROPERTY_DEFINITION_value(sk, i);
327            r->has_optional |= r->properties[i].optional;
328
329            /* Check for duplicated names */
330            if (i > 0 && r->properties[i].name_idx == prev_name_idx) {
331                OPENSSL_free(r);
332                ERR_raise_data(ERR_LIB_PROP, PROP_R_PARSE_FAILED,
333                               "Duplicated name `%s'",
334                               ossl_property_name_str(ctx, prev_name_idx));
335                return NULL;
336            }
337            prev_name_idx = r->properties[i].name_idx;
338        }
339        r->num_properties = n;
340    }
341    return r;
342}
343
344OSSL_PROPERTY_LIST *ossl_parse_property(OSSL_LIB_CTX *ctx, const char *defn)
345{
346    OSSL_PROPERTY_DEFINITION *prop = NULL;
347    OSSL_PROPERTY_LIST *res = NULL;
348    STACK_OF(OSSL_PROPERTY_DEFINITION) *sk;
349    const char *s = defn;
350    int done;
351
352    if (s == NULL || (sk = sk_OSSL_PROPERTY_DEFINITION_new(&pd_compare)) == NULL)
353        return NULL;
354
355    s = skip_space(s);
356    done = *s == '\0';
357    while (!done) {
358        const char *start = s;
359
360        prop = OPENSSL_malloc(sizeof(*prop));
361        if (prop == NULL)
362            goto err;
363        memset(&prop->v, 0, sizeof(prop->v));
364        prop->optional = 0;
365        if (!parse_name(ctx, &s, 1, &prop->name_idx))
366            goto err;
367        prop->oper = OSSL_PROPERTY_OPER_EQ;
368        if (prop->name_idx == 0) {
369            ERR_raise_data(ERR_LIB_PROP, PROP_R_PARSE_FAILED,
370                           "Unknown name HERE-->%s", start);
371            goto err;
372        }
373        if (match_ch(&s, '=')) {
374            if (!parse_value(ctx, &s, prop, 1)) {
375                ERR_raise_data(ERR_LIB_PROP, PROP_R_NO_VALUE,
376                               "HERE-->%s", start);
377                goto err;
378            }
379        } else {
380            /* A name alone means a true Boolean */
381            prop->type = OSSL_PROPERTY_TYPE_STRING;
382            prop->v.str_val = OSSL_PROPERTY_TRUE;
383        }
384
385        if (!sk_OSSL_PROPERTY_DEFINITION_push(sk, prop))
386            goto err;
387        prop = NULL;
388        done = !match_ch(&s, ',');
389    }
390    if (*s != '\0') {
391        ERR_raise_data(ERR_LIB_PROP, PROP_R_TRAILING_CHARACTERS,
392                       "HERE-->%s", s);
393        goto err;
394    }
395    res = stack_to_property_list(ctx, sk);
396
397err:
398    OPENSSL_free(prop);
399    sk_OSSL_PROPERTY_DEFINITION_pop_free(sk, &pd_free);
400    return res;
401}
402
403OSSL_PROPERTY_LIST *ossl_parse_query(OSSL_LIB_CTX *ctx, const char *s,
404                                     int create_values)
405{
406    STACK_OF(OSSL_PROPERTY_DEFINITION) *sk;
407    OSSL_PROPERTY_LIST *res = NULL;
408    OSSL_PROPERTY_DEFINITION *prop = NULL;
409    int done;
410
411    if (s == NULL || (sk = sk_OSSL_PROPERTY_DEFINITION_new(&pd_compare)) == NULL)
412        return NULL;
413
414    s = skip_space(s);
415    done = *s == '\0';
416    while (!done) {
417        prop = OPENSSL_malloc(sizeof(*prop));
418        if (prop == NULL)
419            goto err;
420        memset(&prop->v, 0, sizeof(prop->v));
421
422        if (match_ch(&s, '-')) {
423            prop->oper = OSSL_PROPERTY_OVERRIDE;
424            prop->optional = 0;
425            if (!parse_name(ctx, &s, 1, &prop->name_idx))
426                goto err;
427            goto skip_value;
428        }
429        prop->optional = match_ch(&s, '?');
430        if (!parse_name(ctx, &s, 1, &prop->name_idx))
431            goto err;
432
433        if (match_ch(&s, '=')) {
434            prop->oper = OSSL_PROPERTY_OPER_EQ;
435        } else if (MATCH(&s, "!=")) {
436            prop->oper = OSSL_PROPERTY_OPER_NE;
437        } else {
438            /* A name alone is a Boolean comparison for true */
439            prop->oper = OSSL_PROPERTY_OPER_EQ;
440            prop->type = OSSL_PROPERTY_TYPE_STRING;
441            prop->v.str_val = OSSL_PROPERTY_TRUE;
442            goto skip_value;
443        }
444        if (!parse_value(ctx, &s, prop, create_values))
445            prop->type = OSSL_PROPERTY_TYPE_VALUE_UNDEFINED;
446
447skip_value:
448        if (!sk_OSSL_PROPERTY_DEFINITION_push(sk, prop))
449            goto err;
450        prop = NULL;
451        done = !match_ch(&s, ',');
452    }
453    if (*s != '\0') {
454        ERR_raise_data(ERR_LIB_PROP, PROP_R_TRAILING_CHARACTERS,
455                       "HERE-->%s", s);
456        goto err;
457    }
458    res = stack_to_property_list(ctx, sk);
459
460err:
461    OPENSSL_free(prop);
462    sk_OSSL_PROPERTY_DEFINITION_pop_free(sk, &pd_free);
463    return res;
464}
465
466/*
467 * Compare a query against a definition.
468 * Return the number of clauses matched or -1 if a mandatory clause is false.
469 */
470int ossl_property_match_count(const OSSL_PROPERTY_LIST *query,
471                              const OSSL_PROPERTY_LIST *defn)
472{
473    const OSSL_PROPERTY_DEFINITION *const q = query->properties;
474    const OSSL_PROPERTY_DEFINITION *const d = defn->properties;
475    int i = 0, j = 0, matches = 0;
476    OSSL_PROPERTY_OPER oper;
477
478    while (i < query->num_properties) {
479        if ((oper = q[i].oper) == OSSL_PROPERTY_OVERRIDE) {
480            i++;
481            continue;
482        }
483        if (j < defn->num_properties) {
484            if (q[i].name_idx > d[j].name_idx) {  /* skip defn, not in query */
485                j++;
486                continue;
487            }
488            if (q[i].name_idx == d[j].name_idx) { /* both in defn and query */
489                const int eq = q[i].type == d[j].type
490                               && memcmp(&q[i].v, &d[j].v, sizeof(q[i].v)) == 0;
491
492                if ((eq && oper == OSSL_PROPERTY_OPER_EQ)
493                    || (!eq && oper == OSSL_PROPERTY_OPER_NE))
494                    matches++;
495                else if (!q[i].optional)
496                    return -1;
497                i++;
498                j++;
499                continue;
500            }
501        }
502
503        /*
504         * Handle the cases of a missing value and a query with no corresponding
505         * definition.  The former fails for any comparison except inequality,
506         * the latter is treated as a comparison against the Boolean false.
507         */
508        if (q[i].type == OSSL_PROPERTY_TYPE_VALUE_UNDEFINED) {
509            if (oper == OSSL_PROPERTY_OPER_NE)
510                matches++;
511            else if (!q[i].optional)
512                return -1;
513        } else if (q[i].type != OSSL_PROPERTY_TYPE_STRING
514                   || (oper == OSSL_PROPERTY_OPER_EQ
515                       && q[i].v.str_val != OSSL_PROPERTY_FALSE)
516                   || (oper == OSSL_PROPERTY_OPER_NE
517                       && q[i].v.str_val == OSSL_PROPERTY_FALSE)) {
518            if (!q[i].optional)
519                return -1;
520        } else {
521            matches++;
522        }
523        i++;
524    }
525    return matches;
526}
527
528void ossl_property_free(OSSL_PROPERTY_LIST *p)
529{
530    OPENSSL_free(p);
531}
532
533/*
534 * Merge two property lists.
535 * If there is a common name, the one from the first list is used.
536 */
537OSSL_PROPERTY_LIST *ossl_property_merge(const OSSL_PROPERTY_LIST *a,
538                                        const OSSL_PROPERTY_LIST *b)
539{
540    const OSSL_PROPERTY_DEFINITION *const ap = a->properties;
541    const OSSL_PROPERTY_DEFINITION *const bp = b->properties;
542    const OSSL_PROPERTY_DEFINITION *copy;
543    OSSL_PROPERTY_LIST *r;
544    int i, j, n;
545    const int t = a->num_properties + b->num_properties;
546
547    r = OPENSSL_malloc(sizeof(*r)
548                       + (t == 0 ? 0 : t - 1) * sizeof(r->properties[0]));
549    if (r == NULL)
550        return NULL;
551
552    r->has_optional = 0;
553    for (i = j = n = 0; i < a->num_properties || j < b->num_properties; n++) {
554        if (i >= a->num_properties) {
555            copy = &bp[j++];
556        } else if (j >= b->num_properties) {
557            copy = &ap[i++];
558        } else if (ap[i].name_idx <= bp[j].name_idx) {
559            if (ap[i].name_idx == bp[j].name_idx)
560                j++;
561            copy = &ap[i++];
562        } else {
563            copy = &bp[j++];
564        }
565        memcpy(r->properties + n, copy, sizeof(r->properties[0]));
566        r->has_optional |= copy->optional;
567    }
568    r->num_properties = n;
569    if (n != t)
570        r = OPENSSL_realloc(r, sizeof(*r) + (n - 1) * sizeof(r->properties[0]));
571    return r;
572}
573
574int ossl_property_parse_init(OSSL_LIB_CTX *ctx)
575{
576    static const char *const predefined_names[] = {
577        "provider",     /* Name of provider (default, legacy, fips) */
578        "version",      /* Version number of this provider */
579        "fips",         /* FIPS validated or FIPS supporting algorithm */
580        "output",       /* Output type for encoders */
581        "input",        /* Input type for decoders */
582        "structure",    /* Structure name for encoders and decoders */
583    };
584    size_t i;
585
586    for (i = 0; i < OSSL_NELEM(predefined_names); i++)
587        if (ossl_property_name(ctx, predefined_names[i], 1) == 0)
588            goto err;
589
590    /*
591     * Pre-populate the two Boolean values. We must do them before any other
592     * values and in this order so that we get the same index as the global
593     * OSSL_PROPERTY_TRUE and OSSL_PROPERTY_FALSE values
594     */
595    if ((ossl_property_value(ctx, "yes", 1) != OSSL_PROPERTY_TRUE)
596        || (ossl_property_value(ctx, "no", 1) != OSSL_PROPERTY_FALSE))
597        goto err;
598
599    return 1;
600err:
601    return 0;
602}
603
604static void put_char(char ch, char **buf, size_t *remain, size_t *needed)
605{
606    if (*remain == 0) {
607        ++*needed;
608        return;
609    }
610    if (*remain == 1)
611        **buf = '\0';
612    else
613        **buf = ch;
614    ++*buf;
615    ++*needed;
616    --*remain;
617}
618
619static void put_str(const char *str, char **buf, size_t *remain, size_t *needed)
620{
621    size_t olen, len, i;
622    char quote = '\0';
623    int quotes;
624
625    len = olen = strlen(str);
626    *needed += len;
627
628    /*
629     * Check to see if we need quotes or not.
630     * Characters that are legal in a PropertyName don't need quoting.
631     * We simply assume all others require quotes.
632     */
633    for (i = 0; i < len; i++)
634        if (!ossl_isalnum(str[i]) && str[i] != '.' && str[i] != '_') {
635            /* Default to single quotes ... */
636            if (quote == '\0')
637                quote = '\'';
638            /* ... but use double quotes if a single is present */
639            if (str[i] == '\'')
640                quote = '"';
641        }
642
643    quotes = quote != '\0';
644    if (*remain == 0) {
645        *needed += 2 * quotes;
646        return;
647    }
648
649    if (quotes)
650        put_char(quote, buf, remain, needed);
651
652    if (*remain < len + 1 + quotes)
653        len = *remain - 1;
654
655    if (len > 0) {
656        memcpy(*buf, str, len);
657        *buf += len;
658        *remain -= len;
659    }
660
661    if (quotes)
662        put_char(quote, buf, remain, needed);
663
664    if (len < olen && *remain == 1) {
665        **buf = '\0';
666        ++*buf;
667        --*remain;
668    }
669}
670
671static void put_num(int64_t val, char **buf, size_t *remain, size_t *needed)
672{
673    int64_t tmpval = val;
674    size_t len = 1;
675
676    if (tmpval < 0) {
677        len++;
678        tmpval = -tmpval;
679    }
680    for (; tmpval > 9; len++, tmpval /= 10);
681
682    *needed += len;
683
684    if (*remain == 0)
685        return;
686
687    BIO_snprintf(*buf, *remain, "%lld", (long long int)val);
688    if (*remain < len) {
689        *buf += *remain;
690        *remain = 0;
691    } else {
692        *buf += len;
693        *remain -= len;
694    }
695}
696
697size_t ossl_property_list_to_string(OSSL_LIB_CTX *ctx,
698                                    const OSSL_PROPERTY_LIST *list, char *buf,
699                                    size_t bufsize)
700{
701    int i;
702    const OSSL_PROPERTY_DEFINITION *prop = NULL;
703    size_t needed = 0;
704    const char *val;
705
706    if (list == NULL) {
707        if (bufsize > 0)
708            *buf = '\0';
709        return 1;
710    }
711    if (list->num_properties != 0)
712        prop = &list->properties[list->num_properties - 1];
713    for (i = 0; i < list->num_properties; i++, prop--) {
714        /* Skip invalid names */
715        if (prop->name_idx == 0)
716            continue;
717
718        if (needed > 0)
719            put_char(',', &buf, &bufsize, &needed);
720
721        if (prop->optional)
722            put_char('?', &buf, &bufsize, &needed);
723        else if (prop->oper == OSSL_PROPERTY_OVERRIDE)
724            put_char('-', &buf, &bufsize, &needed);
725
726        val = ossl_property_name_str(ctx, prop->name_idx);
727        if (val == NULL)
728            return 0;
729        put_str(val, &buf, &bufsize, &needed);
730
731        switch (prop->oper) {
732            case OSSL_PROPERTY_OPER_NE:
733                put_char('!', &buf, &bufsize, &needed);
734                /* fall through */
735            case OSSL_PROPERTY_OPER_EQ:
736                put_char('=', &buf, &bufsize, &needed);
737                /* put value */
738                switch (prop->type) {
739                case OSSL_PROPERTY_TYPE_STRING:
740                    val = ossl_property_value_str(ctx, prop->v.str_val);
741                    if (val == NULL)
742                        return 0;
743                    put_str(val, &buf, &bufsize, &needed);
744                    break;
745
746                case OSSL_PROPERTY_TYPE_NUMBER:
747                    put_num(prop->v.int_val, &buf, &bufsize, &needed);
748                    break;
749
750                default:
751                    return 0;
752                }
753                break;
754            default:
755                /* do nothing */
756                break;
757        }
758    }
759
760    put_char('\0', &buf, &bufsize, &needed);
761    return needed;
762}
763