|
OpenVMS Utility Routines Manual
12.20.1 Encoding
The following is an example of encoding:
BerElement *ber_alloc_t(int options);
|
The
ber_alloc_t()
function constructs and returns BerElement. The null pointer is
returned on error. The options field contains a bitwise-or of options
which are to be used when generating the encoding of this BerElement.
One option is defined and must always be supplied:
#define LBER_USE_DER 0x01
|
When this option is present, lengths will always be encoded in the
minimum number of octets. Note that this option does not cause values
of sets and sequences to be rearranged in tag and byte order, so these
functions are not sufficient for generating DER output as defined in
X.509 and X.680. If the caller takes responsibility for ordering values
of sets and sequences correctly, DER output as defined in X.509 and
X.680 can be produced.
Unrecognized option bits are ignored.
The BerElement returned by
ber_alloc_t()
is initially empty. Calls to
ber_printf()
will append bytes to the end of the BerElement.
int ber_printf(BerElement *ber, char *fmt, ... )
|
The
ber_printf()
function is used to encode a BER element in much the same way that
sprintf()
works. One important difference, though, is that state information is
kept in the BER argument so that multiple calls can be made to
ber_printf()
to append to the end of the BER element. BER must be a pointer to a
BerElement returned by
ber_alloc_t()
. The
ber_printf()
function interprets and formats its arguments according to the format
string
fmt
. The
ber_printf()
function returns -1 if there is an error during encoding and a positive
number if successful. As with
sprintf()
, each character in fmt refers to an argument to
ber_printf()
.
The format string can contain the following format characters:
t
|
Tag. The next argument is a
ber_tag_t
specifying the tag to override the next element to be written to the
ber. This works across calls. The value must contain the tag class,
constructed bit, and tag value. The tag value must fit in a single
octet (tag value is less than 32). For example, a tag of "[3]" for a
constructed type is 0xA3.
|
b
|
Boolean. The next argument is a
ber_int_t
, containing either 0 for FALSE or 0xff for TRUE. A boolean element is
output. If this format character is not preceded by the 't' format
modifier, the tag 0x01 is used for the element.
|
e
|
Enumerated. The next argument is a
ber_int_t
, containing the enumerated value in the host's byte order. An
enumerated element is output. If this format character is not preceded
by the 't' format modifier, the tag 0x0A is used for the element.
|
i
|
Integer. The next argument is a
ber_int_t
, containing the integer in the host's byte order. An integer element
is output. If this format character is not preceded by the 't' format
modifier, the tag 0x02 is used for the element.
|
B
|
Bitstring. The next two arguments are a char * pointer to the start of
the bitstring, followed by a
ber_len_t
containing the number of bits in the bitstring. A bitstring element is
output, in primitive form. If this format character is not preceded by
the 't' format modifier, the tag 0x03 is used for the element.
|
n
|
Null. No argument is required. An ASN.1 NULL element is output. If this
format character is not preceded by the 't' format modifier, the tag
0x05 is used for the element.
|
o
|
Octet string. The next two arguments are a char *, followed by a
ber_len_t
with the length of the string. The string may contain null bytes and
need not by zero-terminated. An octet string element is output, in
primitive form. If this format character is not preceded by the 't'
format modifier, the tag 0x04 is used for the element.
|
s
|
Octet string. The next argument is a char * pointing to a
zero-terminated string. An octet string element in primitive form is
output, which does not include the trailing '\0' byte. If this format
character is not preceded by the 't' format modifier, the tag 0x04 is
used for the element.
|
v
|
Several octet strings. The next argument is a char **, an array of char
* pointers to zero-terminated strings. The last element in the array
must be a null pointer. The octet strings do not include the leading
SEQUENCE OF octet strings. The 't' format modifier cannot be used with
this format character.
|
V
|
Several octet strings. A NULL-terminated array of struct berval *'s is
supplied. Note that a construct like '{V}' is required to get an actual
SEQUENCE OF octet strings. The 't' format modifier cannot be used with
this format character.
|
{
|
Begin sequence. No argument is required. If this format character is
not preceded by the 't' format modifier, the tag 0x30 is used.
|
}
|
End sequence. No argument is required. The 't' format modifier cannot
be used with this format character.
|
[
|
Begin set. No argument is required. If this format character is not
preceded by the 't' format modifier, the tag 0x31 is used.
|
]
|
End set. No argument is required. The 't' format modifier cannot be
used with this format character.
|
Each use of a '{' format character must be matched by a '}' character,
either later in the format string, or in the format string of a
subsequent call to
ber_printf()
for that BerElement. The same applies to the '[' and ']'.
Sequences and sets nest, and implementations of this API must maintain
internal state to be able to properly calculate the lengths.
int ber_flatten (BerElement *ber, struct berval **bvPtr);
|
The
ber_flatten()
function allocates a struct berval whose contents are a BER encoding
taken from the ber argument. The bvPtr pointer points to the returned
berval, which must be freed using
ber_bvfree()
. This function returns 0 on success and -1 on error.
The
ber_flatten()
API call is not present in U-M LDAP 3.3.
The use of
ber_flatten()
on a BerElement in which all '{' and '}' format modifiers have not been
properly matched is an error (that is, -1 will be returned by
ber_flatten()
if this situation is exists).
12.20.1.1 Encoding Example
The following is an example of encoding the following ASN.1 data type:
Example1Request ::= SEQUENCE {
s OCTET STRING, -- must be printable
val1 INTEGER,
val2 [0] INTEGER DEFAULT 0
}
int encode_example1(char *s,ber_int_t val1,ber_int_t val2,
struct berval **bvPtr)
{
BerElement *ber;
int rc;
ber = ber_alloc_t(LBER_USE_DER);
if (ber == NULL) return -1;
if (ber_printf(ber,"{si",s,val1) == -1) {
ber_free(ber,1);
return -1;
}
if (val2 != 0) {
if (ber_printf(ber,"ti",(ber_tag_t)0x80,val2) == -1) {
ber_free(ber,1);
return -1;
}
}
if (ber_printf(ber,"}") == -1) {
ber_free(ber,1);
return -1;
}
rc = ber_flatten(ber,bvPtr);
ber_free(ber,1);
return rc;
}
|
12.20.2 Decoding
The following two symbols are available to applications.
#define LBER_ERROR 0xffffffffL
#define LBER_DEFAULT 0xffffffffL
BerElement *ber_init (struct berval *bv);
|
The
ber_init()
function constructs a BerElement and returns a new BerElement
containing a copy of the data in the bv argument. The
ber_init()
function returns the null pointer on error.
ber_tag_t ber_scanf (BerElement *ber, char *fmt, ... );
|
The
ber_scanf()
function is used to decode a BER element in much the same way that
sscanf()
works. One important difference, though, is that some state information
is kept with the ber argument so that multiple calls can be made to
ber_scanf()
to sequentially read from the BER element. The ber argument must be a
pointer to a BerElement returned by
ber_init()
. The
ber_scanf()
function interprets function the bytes according to the format string
fmt, and stores the results in its additional arguments. The
ber_scanf()
function returns LBER_ERROR on error, and a different value on success.
The format string contains conversion specifications which are used to
direct the interpretation of the BER element. The format string can
contain the following characters:
a
|
Octet string. A char ** argument should be supplied. Memory is
allocated, filled with the contents of the octet string, null-
terminated, and the pointer to the string is stored in the argument.
The returned value must be freed using
ldap_memfree()
. The tag of the element must indicate the primitive form (constructed
strings are not supported) but is otherwise ignored and discarded
during the decoding. This format cannot be used with octet strings
which could contain null bytes.
|
O
|
Octet string. A struct berval ** argument should be supplied, which
upon return points to a allocated struct berval containing the octet
string and its length. The
ber_bvfree()
function must be called to free the allocated memory. The tag of the
element must indicate the primitive form (constructed strings are not
supported) but is otherwise ignored during the decoding.
|
b
|
Boolean. A pointer to a
ber_int_t
should be supplied. The value stored will be 0 for FALSE or nonzero for
TRUE. The tag of the element must indicate the primitive form but is
otherwise ignored during the decoding.
|
e
|
Enumerated value stored will be in host byte order. The tag of the
element must indicate the primitive form but is otherwise ignored
during the decoding. The
ber_scanf()
function will return an error if the enumerated value cannot be stored
in a
ber_int_t
.
|
i
|
Integer. A pointer to a
ber_int_t
should be supplied. The value stored will be in host byte order. The
tag of the element must indicate the primitive form but is otherwise
ignored during the decoding. The
ber_scanf()
function will return an error if the integer cannot be stored in a
ber_int_t
.
|
B
|
Bitstring. A char ** argument should be supplied which will point to
the allocated bits, followed by a
ber_len_t
* argument, which will point to the length (in bits) of the bit-string
returned. The
ldap_memfree()
function must be called to free the bit-string. The tag of the element
must indicate the primitive form (constructed bitstrings are not
supported) but is otherwise ignored during the decoding.
|
n
|
Null. No argument is required. The element is simply skipped if it is
recognized as a zero-length element. The tag is ignored.
|
v
|
Several octet strings. A char *** argument should be supplied, which
upon return points to a allocated null-terminated array of char *'s
containing the octet strings. NULL is stored if the sequence is empty.
The
ldap_memfree()
function must be called to free each element of the array and the array
itself. The tag of the sequence and of the octet strings are ignored.
|
V
|
Several octet strings (which could contain null bytes). A struct berval
*** should be supplied, which upon return points to a allocated
null-terminated array of struct berval *'s containing the octet strings
and their lengths. NULL is stored if the sequence is empty. The
ber_bvecfree()
function can be called to free the allocated memory. The tag of the
sequence and of the octet strings are ignored.
|
x
|
Skip element. The next element is skipped. No argument is required.
|
{
|
Begin sequence. No argument is required. The initial sequence tag and
length are skipped.
|
}
|
End sequence. No argument is required.
|
[
|
Begin set. No argument is required. The initial set tag and length are
skipped.
|
]
|
End set. No argument is required.
|
ber_tag_t ber_peek_tag (BerElement *ber, ber_len_t *lenPtr);
|
The
ber_peek_tag()
function returns the tag of the next element to be parsed in the
BerElement argument. The length of this element is stored in the
*lenPtr argument. LBER_DEFAULT is returned if there is no further data
to be read. The ber argument is not modified.
ber_tag_t ber_skip_tag (BerElement *ber, ber_len_t *lenPtr);
|
The
ber_skip_tag()
function is similar to
ber_peek_tag()
, except that the state pointer in the BerElement argument is advanced
past the first tag and length, and is pointed to the value part of the
next element. This function should only be used with constructed types
and situations when a BER encoding is used as the value of an OCTET
STRING. The length of the value is stored in *lenPtr.
ber_tag_t ber_first_element(BerElement *ber,
ber_len_t *lenPtr, char **opaquePtr);
ber_tag_t ber_next_element (BerElement *ber,
ber_len_t *lenPtr, char *opaque);
|
The
ber_first_element()
and
ber_next_element()
functions are used to traverse a SET, SET OF, SEQUENCE or SEQUENCE OF
data value. The
ber_first_element()
function calls
ber_skip_tag()
, stores internal information in *lenPtr and *opaquePtr, and calls
ber_peek_tag()
for the first element inside the constructed value. LBER_DEFAULT is
returned if the constructed value is empty. The
ber_next_element()
function positions the state at the start of the next element in the
constructed type. LBER_DEFAULT is returned if there are no further
values.
The len and opaque values should not be used by applications other than
as arguments to
ber_next_element()
, as shown in the following example:
12.20.2.1 Decoding Example
The following is an example of decoding an ASN.1 data type:
Example2Request ::= SEQUENCE {
dn OCTET STRING, -- must be printable
scope ENUMERATED { b (0), s (1), w (2) },
ali ENUMERATED { n (0), s (1), f (2), a (3) },
size INTEGER,
time INTEGER,
tonly BOOLEAN,
attrs SEQUENCE OF OCTET STRING, -- must be printable
[0] SEQUENCE OF SEQUENCE {
type OCTET STRING -- must be printable,
crit BOOLEAN DEFAULT FALSE,
value OCTET STRING
} OPTIONAL }
#define TAG_CONTROL_LIST 0xA0U /* context specific cons 0 */
int decode_example2(struct berval *bv)
{
BerElement *ber;
ber_len_t len;
ber_tag_t res;
ber_int_t scope, ali, size, time, tonly;
char *dn = NULL, **attrs = NULL;
int i,rc = 0;
ber = ber_init(bv);
if (ber == NULL) {
fputs("ERROR ber_init failed\n", stderr);
return -1;
}
res = ber_scanf(ber,"{aiiiib{v}",&dn,&scope,&ali,
&size,&time,&tonly,&attrs);
if (res == LBER_ERROR) {
fputs("ERROR ber_scanf failed\n", stderr);
ber_free(ber,1);
return -1;
}
/* *** use dn */
ldap_memfree(dn);
for (i = 0; attrs != NULL && attrs[i] != NULL; i++) {
/* *** use attrs[i] */
ldap_memfree(attrs[i]);
}
ldap_memfree(attrs);
if (ber_peek_tag(ber,&len) == TAG_CONTROL_LIST) {
char *opaque;
ber_tag_t tag;
for (tag = ber_first_element(ber,&len,&opaque);
tag != LBER_DEFAULT;
tag = ber_next_element (ber,&len,opaque)) {
ber_len_t tlen;
ber_tag_t ttag;
char *type;
ber_int_t crit;
struct berval *value;
if (ber_scanf(ber,"{a",&type) == LBER_ERROR) {
fputs("ERROR cannot parse type\n",
stderr);
break;
}
/* *** use type */
ldap_memfree(type);
ttag = ber_peek_tag(ber,&tlen);
if (ttag == 0x01U) { /* boolean */
if (ber_scanf(ber,"b",
&crit) == LBER_ERROR){
fputs("ERROR cannot parse crit\n",
stderr);
rc = -1;
break;
}
} else if (ttag == 0x04U) { /* octet string */
crit = 0;
} else {
fputs("ERROR extra field in controls\n",
stderr );
break;
}
if (ber_scanf(ber,"O}",&value) == LBER_ERROR) {
fputs("ERROR cannot parse value\n",
stderr);
rc = -1;
break;
}
/* *** use value */
ber_bvfree(value);
}
}
if ( rc == 0 ) { /* no errors so far */
if (ber_scanf(ber,"}") == LBER_ERROR) {
rc = -1;
}
}
ber_free(ber,1);
return rc;
}
|
12.21 Sample LDAP API Code
#include <ldap.h>
main()
{
LDAP *ld;
LDAPMessage *res, *e;
int i, rc;
char *a, *dn;
BerElement *ptr;
char **vals;
/* open an LDAP session */
if ( (ld = ldap_init( "dotted.host.name", ldap_PORT )) == NULL )
exit( 1 );
/* authenticate as nobody */
if (( rc = ldap_simple_bind_s( ld, NULL, NULL )) != ldap_SUCCESS ) {
fprintf( stderr, "ldap_simple_bind_s: %s\n",
ldap_err2string( rc ));
exit( 1 );
}
/* search for entries with cn of "Babs Jensen", return all attrs */
if (( rc = ldap_search_s( ld, "o=University of Michigan, c=US",
ldap_SCOPE_SUBTREE, "(cn=Babs Jensen)", NULL, 0, &res ))
!= ldap_SUCCESS ) {
fprintf( stderr, "ldap_search_s: %s\n",
ldap_err2string( rc ));
exit( 1 );
}
/* step through each entry returned */
for ( e = ldap_first_entry( ld, res ); e != NULL;
e = ldap_next_entry( ld, e ) ) {
/* print its name */
dn = ldap_get_dn( ld, e );
printf( "dn: %s\n", dn );
ldap_memfree( dn );
/* print each attribute */
for ( a = ldap_first_attribute( ld, e, &ptr ); a != NULL;
a = ldap_next_attribute( ld, e, ptr ) ) {
printf( "attribute: %s\n", a );
/* print each value */
vals = ldap_get_values( ld, e, a );
for ( i = 0; vals[i] != NULL; i++ ) {
printf( "value: %s\n", vals[i] );
}
ldap_value_free( vals );
ldap_memfree( a );
}
if ( ptr != NULL ) {
ber_free( ptr, 0 );
}
}
/* free the search results */
ldap_msgfree( res );
/* close and free connection resources */
ldap_unbind( ld );
}
|
|