Back to Parsing Search Results
This section describes routines which may be used to encode and decode BER-encoded ASN.1 values, which are often used inside of control and extension values.
With the exceptions of two new functions ber_flatten() and ber_init(), these functions are compatible with the University of Michigan LDAP 3.3 implementation of BER.
struct berval { unsigned long bv_len; char *bv_val; };
A struct berval contains a sequence of bytes and an indication of its length. The bv_val is not null terminated. bv_len must always be a nonnegative number. Applications may allocate their own berval structures.
typedef struct berelement { /* opaque */ } BerElement;
The BerElement structure contains not only a copy of the encoded value, but also state information used in encoding or decoding. Applications cannot allocate their own BerElement structures. The internal state is neither thread-specific nor locked, so two threads should not manipulate the same BerElement value simultaneously.
A single BerElement value cannot be used for both encoding and decoding.
void ber_bvfree ( struct berval *bv);
ber_bvfree() frees a berval returned from this API. Both the bv->bv_val string and the berval itself are freed. Applications should not use ber_bvfree() with bervals which the application has allocated.
void ber_bvecfree ( struct berval **bv );
ber_bvecfree() frees an array of bervals returned from this API. Each of the bervals in the array are freed using ber_bvfree(), then the array itself is freed.
struct berval *ber_bvdup (struct berval *bv );
ber_bvdup() returns a copy of a berval. The bv_val field in the returned berval points to a different area of memory as the bv_val field in the argument berval. The null pointer is returned on error (e.g. out of memory).
void ber_free ( BerElement *ber, int fbuf ); void ldap_ber_free ( BerElement *ber, int fbuf );
ber_free() and ldap_ber_free() free a BerElement which is returned from the API calls ber_alloc_t() or ber_init(). Each BerElement must be freed by the caller. The second argument fbuf should always be set to 1.
BerElement *ber_alloc_t(int options);
ber_alloc_t() 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 suitable for generating DER output as defined in X.509 and X.680.
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 ber_alloc_t().
int ber_printf(BerElement *ber, char *fmt, ... )
The ber_printf() routine 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(). ber_printf() interprets and formats its arguments according to the format string fmt. ber_printf() returns -1 if there is an error during encoding. As with sprintf(), each character in fmt refers to an argument to ber_printf().
The format string can contain the following format characters:
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 routine 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 routine 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 can result in a berval whose contents are not a valid BER encoding.
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,int val1,int 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",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 -1; }
The following two symbols are available to applications.
#define LBER_ERROR 0xffffffffL #define LBER_DEFAULT 0xffffffffL BerElement *ber_create (struct berval *bv);
The ber_init functions construct BerElement and returns a new BerElement containing a copy of the data in the bv argument. ber_init returns the null pointer on error.
unsigned long ber_scanf (BerElement *ber, char *fmt, ... );
The ber_scanf() routine 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(). ber_scanf interprets the bytes according to the format string fmt, and stores the results in its additional arguments. ber_scanf() returns LBER_ERROR on error, and a nonnegative number 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:
unsigned long ber_peek_tag (BerElement *ber, unsigned long *lenPtr);
ber_peek_tag() 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.
unsigned long ber_skip_tag (BerElement *ber, unsigned long *lenPtr);
ber_skip_tag() 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 routine 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.
unsigned long ber_first_element(BerElement *ber, unsigned long *lenPtr, char **opaquePtr); unsigned long ber_next_element (BerElement *ber, unsigned long *lenPtr, char *opaque);
ber_first_element() and ber_next_element() are used to traverse a SET, SET OF, SEQUENCE or SEQUENCE OF data value. ber_first_element() 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. ber_next_element() 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 example below.
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 LDAP_TAG_CONTROL_LIST 0xA0L /* context specific cons 0 */ int decode_example2(struct berval *bv) { BerElement *ber; unsigned long len; int scope, ali, size, time, tonly; char *dn = NULL, **attrs = NULL; int res,i,rc = 0; ber = ber_init(bv); if (ber == NULL) { printf("ERROR ber_init failed0); return -1; } res = ber_scanf(ber,"{aiiiiib{v}",&dn,&scope,&ali, &size,&time,&tonly,&attrs); if (res == -1) { printf("ERROR ber_scanf failed0); 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) == LDAP_TAG_CONTROL_LIST) { char *opaque; unsigned long tag; for (tag = ber_first_element(ber,&len,&opaque); tag != LBER_DEFAULT; tag = ber_next_element (ber,&len,opaque)) { unsigned long ttag, tlen; char *type; int crit; struct berval *value; if (ber_scanf(ber,"{a",&type) == LBER_ERROR) { printf("ERROR cannot parse type0); break; } /* *** use type */ ldap_memfree(type); ttag = ber_peek_tag(ber,&tlen); if (ttag == 0x01) { /* boolean */ if (ber_scanf(ber,"b", &crit) == LBER_ERROR) { printf("ERROR cannot parse crit0); rc = -1; break; } } else if (ttag == 0x04) { /* octet string */ crit = 0; } else { printf("ERROR extra field in controls0); break; } if (ber_scanf(ber,"O}",&value) == LBER_ERROR) { printf("ERROR cannot parse value0); rc = -1; break; } /* *** use value */ ldap_bvfree(value); } } ber_scanf(ber,"}"); ber_free(ber,1); return rc; }