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