summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorIngo Schwarze <schwarze@cvs.openbsd.org>2018-03-01 19:20:10 +0000
committerIngo Schwarze <schwarze@cvs.openbsd.org>2018-03-01 19:20:10 +0000
commit14d92350ce66dcf8ec14823a0ae4262f3d848cd9 (patch)
treeca3ab23f8ea3e32b4af309eec2aee2cca927917c /lib
parent4efb71344a2a9ff542ea66115ad4f3d6aaeff4ca (diff)
Two new manual pages OPENSSL_sk_new(3) and STACK_OF(3), written from
scratch, tweaks and OK jsing@ and jmc@.
Diffstat (limited to 'lib')
-rw-r--r--lib/libcrypto/man/Makefile4
-rw-r--r--lib/libcrypto/man/OPENSSL_sk_new.3563
-rw-r--r--lib/libcrypto/man/STACK_OF.3183
3 files changed, 749 insertions, 1 deletions
diff --git a/lib/libcrypto/man/Makefile b/lib/libcrypto/man/Makefile
index 248bd33c8d9..58b3cc28472 100644
--- a/lib/libcrypto/man/Makefile
+++ b/lib/libcrypto/man/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.135 2018/02/25 20:26:51 schwarze Exp $
+# $OpenBSD: Makefile,v 1.136 2018/03/01 19:20:09 schwarze Exp $
.include <bsd.own.mk>
@@ -157,6 +157,7 @@ MAN= \
OPENSSL_config.3 \
OPENSSL_load_builtin_modules.3 \
OPENSSL_malloc.3 \
+ OPENSSL_sk_new.3 \
OpenSSL_add_all_algorithms.3 \
PEM_bytes_read_bio.3 \
PEM_read.3 \
@@ -202,6 +203,7 @@ MAN= \
SHA1.3 \
SMIME_read_PKCS7.3 \
SMIME_write_PKCS7.3 \
+ STACK_OF.3 \
SXNET_new.3 \
TS_REQ_new.3 \
UI_UTIL_read_pw.3 \
diff --git a/lib/libcrypto/man/OPENSSL_sk_new.3 b/lib/libcrypto/man/OPENSSL_sk_new.3
new file mode 100644
index 00000000000..5fe87b8fca1
--- /dev/null
+++ b/lib/libcrypto/man/OPENSSL_sk_new.3
@@ -0,0 +1,563 @@
+.\" $OpenBSD: OPENSSL_sk_new.3,v 1.1 2018/03/01 19:20:09 schwarze Exp $
+.\"
+.\" Copyright (c) 2018 Ingo Schwarze <schwarze@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: March 1 2018 $
+.Dt OPENSSL_SK_NEW 3
+.Os
+.Sh NAME
+.Nm sk_new_null ,
+.Nm sk_new ,
+.Nm sk_set_cmp_func ,
+.Nm sk_dup ,
+.Nm sk_free ,
+.Nm sk_pop_free ,
+.Nm sk_num ,
+.Nm sk_value ,
+.Nm sk_find ,
+.Nm sk_find_ex ,
+.Nm sk_sort ,
+.Nm sk_is_sorted ,
+.Nm sk_push ,
+.Nm sk_unshift ,
+.Nm sk_insert ,
+.Nm sk_set ,
+.Nm sk_pop ,
+.Nm sk_shift ,
+.Nm sk_delete ,
+.Nm sk_delete_ptr ,
+.Nm sk_zero
+.Nd variable-sized arrays of void pointers, called OpenSSL stacks
+.Sh SYNOPSIS
+.In openssl/stack.h
+.Ft _STACK *
+.Fn sk_new_null void
+.Ft _STACK *
+.Fo sk_new
+.Fa "int (*compfunc)(const void *, const void *)"
+.Fc
+.Ft old_function_pointer
+.Fo sk_set_cmp_func
+.Fa "_STACK *stack"
+.Fa "int (*compfunc)(const void *, const void *)"
+.Fc
+.Ft _STACK *
+.Fo sk_dup
+.Fa "_STACK *stack"
+.Fc
+.Ft void
+.Fo sk_free
+.Fa "_STACK *stack"
+.Fc
+.Ft void
+.Fo sk_pop_free
+.Fa "_STACK *stack"
+.Fa "void (*freefunc)(void *)"
+.Fc
+.Ft int
+.Fo sk_num
+.Fa "const _STACK *stack"
+.Fc
+.Ft void *
+.Fo sk_value
+.Fa "const _STACK *stack"
+.Fa "int index"
+.Fc
+.Ft int
+.Fo sk_find
+.Fa "_STACK *stack"
+.Fa "void *wanted"
+.Fc
+.Ft int
+.Fo sk_find_ex
+.Fa "_STACK *stack"
+.Fa "void *wanted"
+.Fc
+.Ft void
+.Fo sk_sort
+.Fa "_STACK *stack"
+.Fc
+.Ft int
+.Fo sk_is_sorted
+.Fa "const _STACK *stack"
+.Fc
+.Ft int
+.Fo sk_push
+.Fa "_STACK *stack"
+.Fa "void *new_item"
+.Fc
+.Ft int
+.Fo sk_unshift
+.Fa "_STACK *stack"
+.Fa "void *new_item"
+.Fc
+.Ft int
+.Fo sk_insert
+.Fa "_STACK *stack"
+.Fa "void *new_item"
+.Fa "int index"
+.Fc
+.Ft void *
+.Fo sk_set
+.Fa "_STACK *stack"
+.Fa "int index"
+.Fa "void *new_item"
+.Fc
+.Ft void *
+.Fo sk_pop
+.Fa "_STACK *stack"
+.Fc
+.Ft void *
+.Fo sk_shift
+.Fa "_STACK *stack"
+.Fc
+.Ft void *
+.Fo sk_delete
+.Fa "_STACK *stack"
+.Fa "int index"
+.Fc
+.Ft void *
+.Fo sk_delete_ptr
+.Fa "_STACK *stack"
+.Fa "void *wanted"
+.Fc
+.Ft void
+.Fo sk_zero
+.Fa "_STACK *stack"
+.Fc
+.Sh DESCRIPTION
+OpenSSL introduced an idiosyncratic concept of variable sized arrays
+of pointers and somewhat misleadingly called such an array a
+.Dq stack .
+Intrinsically, and as documented in this manual page, OpenSSL stacks
+are not type safe but only handle
+.Vt void *
+function arguments and return values.
+.Pp
+OpenSSL also provides a fragile, unusually complicated system of
+macro-generates wrappers that offers superficial type safety at the
+expense of extensive obfuscation, implemented using large amounts
+of autogenerated code involving exceedingly ugly, nested
+.Xr cpp 1
+macros; see the
+.Xr STACK_OF 3
+manual page for details.
+.Pp
+The fundamental data type is the
+.Vt _STACK
+structure.
+It stores a variable number of void pointers
+and remembers the number of pointers currently stored.
+It can optionally hold a pointer to a comparison function.
+As long as no comparison function is installed, the order of pointers
+is meaningful; as soon as a comparison function is installed, it
+becomes ill-defined.
+.Pp
+.Fn sk_new_null
+allocates and initializes a new, empty stack.
+.Fn sk_new
+is identical except that it also installs
+.Fa compfunc
+as the comparison function for the new stack object.
+.Fn sk_set_cmp_func
+installs
+.Fa compfunc
+for the existing
+.Fa stack .
+The
+.Fa compfunc
+is allowed to be
+.Dv NULL ,
+but the
+.Fa stack
+is not.
+.Pp
+.Fn sk_dup
+creates a shallow copy of the given
+.Fa stack ,
+which must not be a
+.Dv NULL
+pointer.
+It neither copies the objects pointed to from the stack nor
+increases their reference counts, but merely copies the pointers.
+Extreme care must be taken in order to avoid freeing the memory twice,
+for example by calling
+.Fn sk_free
+on one copy and only calling
+.Fn sk_pop_free
+on the other.
+.Pp
+.Fn sk_free
+frees the given
+.Fa stack .
+It does not free any of the pointers stored on the stack.
+Unless these pointers are merely copies of pointers owner by
+other objects, they must be freed before calling
+.Fn sk_free ,
+in order to avoid leaking memory.
+If
+.Fa stack
+is a
+.Dv NULL
+pointer, no action occurs.
+.Pp
+.Fn sk_pop_free
+is severely misnamed.
+It does not at all do what one would expect from a function called
+.Dq pop .
+Instead, it does the same as
+.Fn sk_free ,
+except that it also calls the function
+.Fa freefunc
+on each of the pointers contained in the
+.Fa stack .
+If the calls to
+.Fa freefunc
+are intended to free the memory in use by the objects on the stack,
+ensure that no other pointers to the same objects remain elsewhere.
+.Pp
+.Fn sk_find
+searches the
+.Fa stack
+for the
+.Fa wanted
+pointer.
+If the
+.Fa stack
+contains more than one copy of the
+.Fa wanted
+pointer, only the first match is found.
+If a comparison function is installed for the stack, the stack is
+first sorted with
+.Fn sk_sort ,
+and instead of comparing pointers, two pointers are considered to match
+if the comparison function returns 0.
+.Pp
+.Fn sk_find_ex
+is identical to
+.Fn sk_find
+except that if the
+.Fa stack
+is not empty but no match is found,
+the index of some pointer considered closest to
+.Fa wanted
+is returned.
+.Pp
+.Fn sk_sort
+sorts the
+.Fa stack
+using
+.Xr qsort 3
+and the installed comparison function.
+If
+.Fa stack
+is a
+.Dv NULL
+pointer or already considered sorted, no action occurs.
+This function can only be called if a comparison function is installed.
+.Pp
+.Fn sk_is_sorted
+reports whether the
+.Fa stack
+is considered sorted.
+Calling
+.Fn sk_new_null
+or
+.Fn sk_new ,
+successfuly calling
+.Fn sk_push ,
+.Fn sk_unshift ,
+or
+.Fn sk_insert ,
+or changing the comparison function sets the state to unsorted.
+If a comparison function is installed, calling
+.Fn sk_sort ,
+.Fn sk_find ,
+or
+.Fn sk_find_ex
+sets the state to sorted.
+.Pp
+.Fn sk_push
+pushes
+.Fa new_item
+onto the end of the
+.Fa stack ,
+increasing the number of pointers by 1.
+If
+.Fa stack
+is a
+.Dv NULL
+pointer, no action occurs.
+.Pp
+.Fn sk_unshift
+inserts
+.Fa new_item
+at the beginning of the
+.Fa stack ,
+such that it gets the index 0.
+The number of pointers increases by 1.
+If
+.Fa stack
+is a
+.Dv NULL
+pointer, no action occurs.
+.Pp
+.Fn sk_insert
+inserts the
+.Fa new_item
+into the
+.Fa stack
+such that it gets the given
+.Fa index .
+If
+.Fa index
+is less than 0 or greater than or equal to
+.Fn sk_num stack ,
+the effect is the same as for
+.Fn sk_push .
+If
+.Fa stack
+is a
+.Dv NULL
+pointer, no action occurs.
+.Pp
+.Fn sk_set
+replaces the pointer with the given
+.Fa index
+on the
+.Fa stack
+with the
+.Fa new_item .
+The old pointer is not freed,
+which may leak memory if no copy of it exists elsewhere.
+If
+.Fa stack
+is a
+.Dv NULL
+pointer or if
+.Fa index
+is less than 0 or greater than or equal to
+.Fn sk_num stack ,
+no action occurs.
+.Pp
+.Fn sk_pop
+and
+.Fn sk_shift
+remove the pointer with the highest or lowest index from the
+.Fa stack ,
+respectively, reducing the number of pointers by 1.
+If
+.Fa stack
+is a
+.Dv NULL
+pointer or if it is empty, no action occurs.
+.Pp
+.Fn sk_delete
+removes the pointer with the given
+.Fa index
+from the
+.Fa stack ,
+reducing the number of pointers by 1.
+If
+.Fa stack
+is a
+.Dv NULL
+pointer or the
+.Fa index
+is less than 0 or greater than or equal to
+.Fn sk_num stack ,
+no action occurs.
+.Pp
+.Fn sk_delete_ptr
+removes the
+.Fa wanted
+pointer from the
+.Fa stack ,
+reducing the number of pointers by 1 if it is found.
+It never uses a comparison function
+but only compares pointers themselves.
+The
+.Fa stack
+pointer must not be
+.Dv NULL .
+.Pp
+.Fn sk_zero
+removes all pointers from the
+.Fa stack .
+It does not free any of the pointers.
+Unless these pointers are merely copies of pointers owner by other
+objects, they must be freed before calling sk_zero(), in order to
+avoid leaking memory.
+If
+.Fa stack
+is a
+.Dv NULL
+pointer, no action occurs.
+.Sh RETURN VALUES
+.Fn sk_new_null ,
+.Fn sk_new ,
+and
+.Fn sk_dup
+return a pointer to the newly allocated stack object or
+.Dv NULL
+if insufficient memory is available.
+.Pp
+.Fn sk_set_cmp_func
+returns a pointer to the comparison function
+that was previously installed for the
+.Fa stack
+or
+.Dv NULL
+if none was installed.
+.Pp
+.Fn sk_num
+returns the number of pointers currently stored on the
+.Fa stack ,
+or \-1 if
+.Fa stack
+is a
+.Dv NULL
+pointer.
+.Pp
+.Fn sk_value
+returns the pointer with the given
+.Fa index
+from the
+.Fa stack ,
+or
+.Dv NULL
+if
+.Fa stack
+is a
+.Dv NULL
+pointer or if the
+.Fa index
+is less than 0 or greater than or equal to
+.Fn sk_num stack .
+.Pp
+.Fn sk_find
+returns the lowest index considered to match or \-1 if
+.Fa stack
+is a
+.Dv NULL
+pointer or if no match is found.
+.Pp
+.Fn sk_find_ex
+returns some index or \-1 if
+.Fa stack
+is a
+.Dv NULL
+pointer or empty.
+.Pp
+.Fn sk_is_sorted
+returns 1 if the
+.Fa stack
+is considered sorted or if it is a
+.Dv NULL
+pointer, or 0 otherwise.
+.Pp
+.Fn sk_push ,
+.Fn sk_unshift ,
+and
+.Fn sk_insert
+return the new number of pointers on the
+.Fa stack
+or 0 if
+.Fa stack
+is a
+.Dv NULL
+pointer or if memory allocation fails.
+.Pp
+.Fn sk_set
+returns
+.Fa new_item
+or
+.Dv NULL
+if
+.Fa stack
+is a
+.Dv NULL
+pointer or if the
+.Fa index
+is less than 0 or greater than or equal to
+.Fn sk_num stack .
+.Pp
+.Fn sk_pop
+and
+.Fn sk_shift
+return the deleted pointer or
+.Dv NULL
+if
+.Fa stack
+is a
+.Dv NULL
+pointer or if it is empty.
+.Pp
+.Fn sk_delete
+returns the deleted pointer or
+.Dv NULL
+if
+.Fa stack
+is a
+.Dv NULL
+pointer or if the
+.Fa index
+is less than 0 or greater than or equal to
+.Fn sk_num stack .
+.Pp
+.Fn sk_delete_ptr
+returns
+.Fa wanted
+or
+.Dv NULL
+if it is not found.
+.Sh BUGS
+.Fn sk_set
+does not set the state of the
+.Fa stack
+to unsorted.
+This can cause wrong results from subsequent
+.Fn sk_find
+and
+.Fn sk_find_ex .
+.Pp
+Even if a comparison function is installed, empty stacks and
+stacks containing a single pointer are sometimes considered
+sorted and sometimes considered unsorted.
+.Pp
+If a comparison function is installed, the concept of
+.Dq first match
+in
+.Fn sk_find
+and
+.Fn sk_find_ex
+is ill-defined because
+.Xr qsort 3
+is not a stable sorting function.
+It is probably best to only assume that they return an arbitrary match.
+.Pp
+The concept of
+.Dq closest
+for
+.Fn sk_find_ex
+is even less clearly defined.
+The match may sometimes be smaller and sometimes larger than
+.Fa wanted ,
+even if both smaller and larger pointers exist in the
+.Fa stack .
+Besides, it is again ill-defined
+which of several pointers that compare equal is selected.
+It is probably best to not assume anything about the selection
+for cases where there is no match.
diff --git a/lib/libcrypto/man/STACK_OF.3 b/lib/libcrypto/man/STACK_OF.3
new file mode 100644
index 00000000000..206d7faec37
--- /dev/null
+++ b/lib/libcrypto/man/STACK_OF.3
@@ -0,0 +1,183 @@
+.\" $OpenBSD: STACK_OF.3,v 1.1 2018/03/01 19:20:09 schwarze Exp $
+.\"
+.\" Copyright (c) 2018 Ingo Schwarze <schwarze@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: March 1 2018 $
+.Dt STACK_OF 3
+.Os
+.Sh NAME
+.Nm STACK_OF
+.Nd variable-sized arrays of pointers, called OpenSSL stacks
+.Sh SYNOPSIS
+.In openssl/safestack.h
+.Fn STACK_OF type
+.Sh DESCRIPTION
+The
+.In openssl/safestack.h
+header provides a fragile, unusually complicated system of
+macro-generated wrappers around the functions described in the
+.Xr OPENSSL_sk_new 3
+manual page.
+It is intended to implement superficially type-safe variable-sized
+arrays of pointers, somewhat misleadingly called
+.Dq stacks
+by OpenSSL.
+Due to the excessive number of API functions, it is impossible to
+properly document this system.
+In particular, calling
+.Xr man 1
+for any of the functions operating on stacks cannot yield any result.
+.Pp
+Unfortunately, application programs can hardly avoid using the concept
+because several important OpenSSL APIs rely on it; see the
+.Sx SEE ALSO
+section for examples.
+Even though both pages are more complicated than any manual page
+ought to be, using the concept safely requires a complete understanding
+of all the details in both this manual page and in
+.Xr OPENSSL_sk_new 3 .
+.Pp
+The
+.Fn STACK_OF
+macro takes a
+.Fa type
+name as its argument, typically the name of a type
+that has been defined as an alias for a specific
+.Vt struct
+type using a
+.Sy typedef
+declaration.
+It expands to an incomplete
+.Vt struct
+type which is intended to represent a
+.Dq stack
+of objects of the given
+.Fa type .
+That type does not actually exist, so it is not possible to define,
+for example, an automatic variable
+.Ql STACK_OF(X509) my_certificates ;
+it is only possible to define pointers to stacks, for example
+.Ql STACK_OF(X509) *my_certificates .
+The only way such pointers can ever be used is by wrapper functions
+casting them to the type
+.Vt _STACK *
+described in
+.Xr OPENSSL_sk_new 3 .
+.Pp
+For a considerable number of types, OpenSSL provides one wrapper
+function for each function described in
+.Xr OPENSSL_sk_new 3 .
+The names of these wrapper functions are usually constructed by
+inserting the name of the type and an underscore after the
+.Sq sk_
+prefix of the function name.
+Usually, where the real functions take
+.Vt void *
+arguments, the wrappers take pointers to the
+.Fa type
+in questions, and where the real functions take
+.Vt _STACK *
+arguments, the wrappers take pointers to
+.Fn STACK_OF type .
+The same applies to return values.
+Various exceptions to all this exist, but the above applies to
+all the types listed below.
+.Pp
+Using the above may make sense for the following types because
+public API functions exist that take stacks of these types as
+arguments or return them:
+.Vt ACCESS_DESCRIPTION ,
+.Vt ASN1_INTEGER ,
+.Vt ASN1_OBJECT ,
+.Vt ASN1_TYPE ,
+.Vt ASN1_UTF8STRING ,
+.Vt CONF_VALUE ,
+.Vt DIST_POINT ,
+.Vt GENERAL_NAME ,
+.Vt GENERAL_SUBTREE ,
+.Vt PKCS12_SAFEBAG ,
+.Vt PKCS7 ,
+.Vt PKCS7_RECIP_INFO ,
+.Vt PKCS7_SIGNER_INFO ,
+.Vt POLICY_MAPPING ,
+.Vt POLICYINFO ,
+.Vt POLICYQUALINFO ,
+.Vt X509 ,
+.Vt X509_ALGOR ,
+.Vt X509_ATTRIBUTE ,
+.Vt X509_CRL ,
+.Vt X509_EXTENSION ,
+.Vt X509_INFO ,
+.Vt X509_OBJECT ,
+.Vt X509_POLICY_NODE ,
+.Vt X509_PURPOSE ,
+.Vt X509_REVOKED .
+.Pp
+Even though the OpenSSL headers declare wrapper functions for many
+more types and even though the OpenSSL documentation says that users
+can declare their own stack types, using
+.Fn STACK_OF
+with any type not listed here is strongly discouraged.
+For other types, there may be subtle, undocumented differences
+in syntax and semantics, and attempting to declare custom stack
+types is very error prone; using plain C arrays of pointers to
+the desired type is much simpler and less dangerous.
+.Sh EXAMPLES
+The following program creates a certificate object, puts two
+pointers to it on a stack, and uses
+.Xr X509_free 3
+to clean up properly:
+.Bd -literal
+#include <err.h>
+#include <stdio.h>
+#include <openssl/x509.h>
+
+int
+main(void)
+{
+ STACK_OF(X509) *stack;
+ X509 *x;
+
+ if ((stack = sk_X509_new_null()) == NULL)
+ err(1, NULL);
+ if ((x = X509_new()) == NULL)
+ err(1, NULL);
+ if (sk_X509_push(stack, x) == 0)
+ err(1, NULL);
+ if (X509_up_ref(x) == 0)
+ errx(1, "X509_up_ref failed");
+ if (sk_X509_push(stack, x) == 0)
+ err(1, NULL);
+ printf("%d pointers: %p, %p\en", sk_X509_num(stack),
+ sk_X509_value(stack, 0), sk_X509_value(stack, 1));
+ sk_X509_pop_free(stack, X509_free);
+
+ return 0;
+}
+.Ed
+.Pp
+The output looks similar to:
+.Pp
+.Dl 2 pointers: 0x4693ff24c00, 0x4693ff24c00
+.Sh SEE ALSO
+.Xr OCSP_request_sign 3 ,
+.Xr PKCS12_parse 3 ,
+.Xr PKCS7_encrypt 3 ,
+.Xr SSL_CTX_set_client_CA_list 3 ,
+.Xr SSL_get_ciphers 3 ,
+.Xr SSL_get_peer_cert_chain 3 ,
+.Xr SSL_load_client_CA_file 3 ,
+.Xr X509_CRL_get_REVOKED 3 ,
+.Xr X509_STORE_CTX_get0_chain 3